<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>가지가지하네</title>
    <link>https://gajicoding.tistory.com/</link>
    <description>gajicoding 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Tue, 30 Jun 2026 10:16:10 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>가지코딩</managingEditor>
    <image>
      <title>가지가지하네</title>
      <url>https://tistory1.daumcdn.net/tistory/7709884/attach/bcd8b31f755d4510ab12fec1c541c26e</url>
      <link>https://gajicoding.tistory.com</link>
    </image>
    <item>
      <title>context-path와 Security: 예상과 다른 경로 매칭 문제</title>
      <link>https://gajicoding.tistory.com/399</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot 프로젝트에서 REST API 경로에 공통 prefix(/api)를 적용하기 위해 context-path 설정을 사용하는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;하지만 이렇게 설정한 뒤, Spring Security에서 특정 경로를 허용하려 할 때, &lt;b&gt;의도한 대로 매칭되지 않고 403 혹은 401 에러&lt;/b&gt;가 발생하는 경우가 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 상황&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 context-path를 설정하고&lt;/p&gt;
&lt;pre id=&quot;code_1752546083301&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server:
  servlet:
    context-path: /api&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Security 설정에서는 아래와 같이 /api/login을 허용하도록 구성했다&lt;/p&gt;
&lt;pre id=&quot;code_1752546112556&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;http
    .authorizeHttpRequests(auth -&amp;gt; auth
        .requestMatchers(&quot;/api/login&quot;).permitAll()
        .anyRequest().authenticated()
    );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우&lt;/p&gt;
&lt;h4 data-end=&quot;885&quot; data-start=&quot;800&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt; 클라이언트에서 /api/login으로 요청을 보내면 &lt;b&gt;403 Forbidden&lt;/b&gt; 또는 &lt;b&gt;401 Unauthorized&lt;/b&gt; 오류가 발생한다. &lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인 분석&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot에서 server.servlet.context-path는 DispatcherServlet 레벨에서 모든 API 경로 앞에 /api prefix를 붙이게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 컨트롤러의 매핑이 다음과 같다면:&lt;/p&gt;
&lt;pre id=&quot;code_1752546258732&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@PostMapping(&quot;/login&quot;)
public ResponseEntity&amp;lt;?&amp;gt; login(@RequestBody LoginRequest request) {
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 호출 가능한 경로는 /api/login이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt; 하지만,&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Security는 context-path를 제거한 경로 기준으로 경로 매칭을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, requestMatchers(&quot;/api/login&quot;)는 &lt;b&gt;실제 Security filter chain에서 존재하지 않는 경로&lt;/b&gt;로 간주된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;Spring Security의 경로 매칭은 HttpServletRequest.getServletPath() 기준으로 이루어지며,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;이 값은 context-path가 제외된 상태이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1752546338567&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;String servletPath = request.getServletPath(); // &quot;/login&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결 방법&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt; Security 설정 시 context-path(prefix)를 &lt;b&gt;포함하지 않고&lt;/b&gt; 경로를 지정 &lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1399&quot; data-start=&quot;1374&quot;&gt;실제 요청은 /api/login이지만,&lt;/li&gt;
&lt;li data-end=&quot;1436&quot; data-start=&quot;1400&quot;&gt;Security 설정에서는 /login으로 매칭해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1752546032832&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;http
    .authorizeHttpRequests(auth -&amp;gt; auth
        .requestMatchers(&quot;/login&quot;).permitAll() // &quot;/api/login&quot; 아님
        .anyRequest().authenticated()
    );&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Spring/문제 해결 (Troubleshooting)</category>
      <author>가지코딩</author>
      <guid isPermaLink="true">https://gajicoding.tistory.com/399</guid>
      <comments>https://gajicoding.tistory.com/399#entry399comment</comments>
      <pubDate>Tue, 15 Jul 2025 11:26:46 +0900</pubDate>
    </item>
    <item>
      <title>@Cacheable 사용 시 역직렬화 오류 해결</title>
      <link>https://gajicoding.tistory.com/398</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 상황&lt;/h2&gt;
&lt;pre id=&quot;code_1752545460006&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Cacheable(value = &quot;books&quot;, key = &quot;...&quot;)
public PagedResponse&amp;lt;BookResponseDto&amp;gt; findAllCached(...) {
    return bookService.findAll(...);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;PagedResponse&amp;lt;BookResponseDto&amp;gt; 와 같은 제네릭 타입으로 캐싱할 때&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;캐시에서 &lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;조회된 데이터가 LinkedHashMap으로 역직렬화&lt;/span&gt;&lt;/b&gt;되어 아래와 같은 오류가 발생&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;java.lang.ClassCastException:&amp;nbsp;class&amp;nbsp;java.util.LinkedHashMap&amp;nbsp;cannot&amp;nbsp;be&amp;nbsp;cast&amp;nbsp;to&amp;nbsp;class&amp;nbsp;co&lt;a href=&quot;http://m.example.book_api.global.dto.PagedResponse&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;http://m.example.book_api.global.dto.PagedResponse&lt;/a&gt;&amp;nbsp;(java.util.LinkedHashMap&amp;nbsp;is&amp;nbsp;in&amp;nbsp;module&amp;nbsp;java.base&amp;nbsp;of&amp;nbsp;loader&amp;nbsp;'bootstrap';&amp;nbsp;co&lt;a href=&quot;http://m.example.book_api.global.dto.PagedResponse&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;http://m.example.book_api.global.dto.PagedResponse&lt;/a&gt;&amp;nbsp;is&amp;nbsp;in&amp;nbsp;unnamed&amp;nbsp;module&amp;nbsp;of&amp;nbsp;loader&amp;nbsp;'app')&lt;/p&gt;
&lt;pre id=&quot;code_1752545263385&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class com.example.book_api.global.dto.PagedResponse (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; com.example.book_api.global.dto.PagedResponse is in unnamed module of loader 'app')&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 원인 분석&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;602&quot; data-start=&quot;528&quot;&gt;@Cacheable 내부에서 Jackson이 객체를 JSON으로 직렬화하고 역직렬화하는 과정에서 제네릭 타입 정보가 손실된다.&lt;/li&gt;
&lt;li data-end=&quot;662&quot; data-start=&quot;603&quot;&gt;Java의 타입 소거(Type Erasure) 특성 때문에 런타임에 제네릭 타입을 정확히 알 수 없어,&lt;/li&gt;
&lt;li data-end=&quot;707&quot; data-start=&quot;663&quot;&gt;역직렬화 시 기본적으로 LinkedHashMap 타입으로 변환하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 해결 방법&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt; 직접 캐시 로직 구현 및 TypeReference 사용&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자동 캐시 대신 RedisTemplate 등으로 직접 캐시를 관리한다.&lt;/li&gt;
&lt;li&gt;ObjectMapper에 TypeReference를 명시해 정확한 타입으로 역직렬화한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1752545690467&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 역 직렬화 문제로 수동 캐싱
public PagedResponse&amp;lt;BookResponseDto&amp;gt; findAllCached(...) {

    // key 조회
    String key = ...;
    Object cached = redisTemplate.opsForValue().get(key);
    PagedResponse&amp;lt;BookResponseDto&amp;gt; cachedResponse = objectMapper.convertValue(
            cached,
            new TypeReference&amp;lt;PagedResponse&amp;lt;BookResponseDto&amp;gt;&amp;gt;() {}
    );

    // 저장된 캐시가 있으면, 캐시 값 출력
    if (cachedResponse != null) {
		...
        return cachedResponse;
    }

    // 저장된 캐시가 없으면, DB 조회 + 캐시 생성
    PagedResponse&amp;lt;BookResponseDto&amp;gt; result = bookService.findAll(...);
    redisTemplate.opsForValue().set(key, result, 30, TimeUnit.MINUTES);

    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring/문제 해결 (Troubleshooting)</category>
      <author>가지코딩</author>
      <guid isPermaLink="true">https://gajicoding.tistory.com/398</guid>
      <comments>https://gajicoding.tistory.com/398#entry398comment</comments>
      <pubDate>Tue, 15 Jul 2025 11:15:45 +0900</pubDate>
    </item>
    <item>
      <title>EXPLAIN ANALYZE 활용법</title>
      <link>https://gajicoding.tistory.com/396</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;EXPLAIN ANALYZE란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL에서 쿼리 실행 계획뿐만 아니라, 쿼리를 실제로 실행해 본 후 &lt;b&gt;실제 실행 시간과 처리된 행 수&lt;/b&gt; 같은 실행 통계까지 함께 보여주는 명령어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단순 EXPLAIN과 달리, 쿼리를 실제 실행하므로 정확한 성능 정보를 알 수 있다.&lt;/li&gt;
&lt;li&gt;옵티마이저가 예측한 실행 계획과 실제 수행 결과를 비교해, 실행 계획의 적절성을 검증할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;EXPLAIN ANALYZE 활용법 및 개선 방향&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;172&quot; data-start=&quot;139&quot;&gt;실제 실행 시간과 옵티마이저 예상 비용을 비교한다.&lt;/li&gt;
&lt;li data-end=&quot;227&quot; data-start=&quot;173&quot;&gt;조인 방식(Nested Loop, Hash Join 등)과 인덱스 활용 여부를 확인한다.&lt;/li&gt;
&lt;li data-end=&quot;263&quot; data-start=&quot;228&quot;&gt;불필요한 컬럼 조회를 피하고, 필요한 컬럼만 선택한다. (커버링 인덱스 유도)&lt;/li&gt;
&lt;li data-end=&quot;308&quot; data-start=&quot;264&quot;&gt;통계 정보를 최신으로 유지해 옵티마이저가 정확한 계획을 세우도록 한다.&lt;/li&gt;
&lt;li data-end=&quot;342&quot; data-start=&quot;309&quot;&gt;변경 전후 성능을 수치로 비교해 튜닝 효과를 검증한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;EXPLAIN ANALYZE 활용 예제&lt;/h2&gt;
&lt;pre id=&quot;code_1751558734560&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;EXPLAIN ANALYZE
SELECT o.order_id, o.total_price, c.customer_name
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE o.total_price &amp;gt; 1000000
AND c.region = 'Seoul';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상 결과 예시&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;533&quot; data-start=&quot;339&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;402&quot; data-start=&quot;339&quot;&gt;orders 테이블이 &lt;b&gt;테이블 스캔&lt;/b&gt;을 수행하고 있음 (cost=15000, 실제 120ms 소요)&lt;/li&gt;
&lt;li data-end=&quot;503&quot; data-start=&quot;403&quot;&gt;customers 테이블 인덱스 조회는 인덱스가 있으나, orders 테이블에서 4800건을 모두 스캔한 후 각각에 대해 4800번 조회 수행 (loops=4800)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1751558917457&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                             |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -&amp;gt; Nested loop join                                                                                                                                                |
|    -&amp;gt; Table scan on orders  (cost=15000 rows=5000) (actual time=120.500..122.700 rows=4800 loops=1)                                                                |
|    -&amp;gt; Index lookup on customers using idx_customers_region (region = 'Seoul')  (cost=100 rows=50) (actual time=0.5..0.6 rows=48 loops=4800)                         |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.130 sec)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;결과 해석&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1867&quot; data-start=&quot;1816&quot;&gt;&lt;b&gt;Nested loop join&lt;/b&gt; 방식으로 두 테이블을 조인함을 확인할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1867&quot; data-start=&quot;1816&quot;&gt;두 테이블을 조인할 때, 외부 테이블(orders)을 한 행씩 순회하면서 내부 테이블(customers)에서 해당 조건에 맞는 행을 찾는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1931&quot; data-start=&quot;1868&quot;&gt;orders 테이블에 대해 풀 테이블 스캔을 수행하고, 그 행(4800개) 각각에 대해 customers 테이블에서 region='Seoul' 인덱스를 이용해 고객을 찾는다.&lt;/li&gt;
&lt;li data-end=&quot;2000&quot; data-start=&quot;1932&quot;&gt;내부 테이블 인덱스 조회가 외부 테이블의 모든 행마다 반복되므로, 비용이 많이 든다. &lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 쿼리 튜닝 방법&lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-end=&quot;96&quot; data-start=&quot;82&quot; data-ke-size=&quot;size18&quot;&gt;1. 조인 방식 변경&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;306&quot; data-start=&quot;98&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;141&quot; data-start=&quot;98&quot;&gt;MySQL 기본 조인 방식은 &lt;b&gt;Nested Loop Join&lt;/b&gt;이다.&lt;/li&gt;
&lt;li data-end=&quot;200&quot; data-start=&quot;142&quot;&gt;큰 테이블을 먼저 필터링하거나 작은 테이블을 먼저 탐색하도록 조인 순서를 조정하여 성능 개선 가능&lt;/li&gt;
&lt;li data-end=&quot;244&quot; data-start=&quot;201&quot;&gt;&lt;b&gt;STRAIGHT_JOIN&lt;/b&gt; 키워드로 조인 순서를 강제할 수 있다.&lt;/li&gt;
&lt;li data-end=&quot;306&quot; data-start=&quot;245&quot;&gt;서브쿼리, 윈도우 함수 등 MySQL 8+ 기능을 활용해 복잡한 조인을 간결하게 표현하고 최적화 시도 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;325&quot; data-start=&quot;313&quot; data-ke-size=&quot;size18&quot;&gt;2. 인덱스 추가&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;529&quot; data-start=&quot;327&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;405&quot; data-start=&quot;327&quot;&gt;조인 조건 컬럼(customer_id 등)과 WHERE 절 필터 컬럼(total_price, region)에 인덱스를 추가&lt;/li&gt;
&lt;li data-end=&quot;455&quot; data-start=&quot;406&quot;&gt;&lt;b&gt;복합 인덱스&lt;/b&gt; 생성으로 다중 컬럼 조건을 한 번에 커버해 디스크 I/O 감소&lt;/li&gt;
&lt;li data-end=&quot;501&quot; data-start=&quot;456&quot;&gt;인덱스 컬럼 순서도 중요, WHERE와 JOIN 조건 우선순위에 맞게 설계&lt;/li&gt;
&lt;li data-end=&quot;529&quot; data-start=&quot;502&quot;&gt;불필요한 인덱스는 제거해 쓰기 성능 저하 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;549&quot; data-start=&quot;536&quot; data-ke-size=&quot;size18&quot;&gt;3. 쿼리 리팩토링&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;753&quot; data-start=&quot;551&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;581&quot; data-start=&quot;551&quot;&gt;필요한 컬럼만 조회 (SELECT * 지양)&lt;/li&gt;
&lt;li data-end=&quot;629&quot; data-start=&quot;582&quot;&gt;복잡한 쿼리는 뷰(View)나 임시 테이블로 분할해 가독성 및 유지보수성 향상&lt;/li&gt;
&lt;li data-end=&quot;672&quot; data-start=&quot;630&quot;&gt;서브쿼리를 조인으로 변환해 옵티마이저가 더 좋은 계획 선택하도록 유도&lt;/li&gt;
&lt;li data-end=&quot;701&quot; data-start=&quot;673&quot;&gt;불필요한 함수 호출, 계산 제거로 비용 절감&lt;/li&gt;
&lt;li data-end=&quot;753&quot; data-start=&quot;702&quot;&gt;LIMIT, OFFSET 등 페이징 조건 최적화 (예: keyset pagination)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;770&quot; data-start=&quot;760&quot; data-ke-size=&quot;size18&quot;&gt;4. 기타 팁&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;870&quot; data-start=&quot;772&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;801&quot; data-start=&quot;772&quot;&gt;ANALYZE TABLE로 통계 정보 갱신&lt;/li&gt;
&lt;li data-end=&quot;848&quot; data-start=&quot;802&quot;&gt;실행 계획과 실제 실행 시간 간 차이가 크면 데이터 분포 및 통계 문제 의심&lt;/li&gt;
&lt;li data-end=&quot;870&quot; data-start=&quot;849&quot;&gt;대용량 데이터는 파티셔닝도 검토&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 리팩토링 후 쿼리 예시 &lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1571&quot; data-start=&quot;1531&quot;&gt;orders 테이블에 total_price에 대한 인덱스 추가&lt;/li&gt;
&lt;li data-end=&quot;1591&quot; data-start=&quot;1572&quot;&gt;서브쿼리로 필터링된 고객만 조인&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1751559249636&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;EXPLAIN ANALYZE
SELECT o.order_id, o.total_price, c.customer_name
FROM orders o
JOIN (
  SELECT customer_id, customer_name
  FROM customers
  WHERE region = 'Seoul'
) c ON o.customer_id = c.customer_id
WHERE o.total_price &amp;gt; 1000000;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상 결과 예시&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;945&quot; data-start=&quot;878&quot;&gt;orders 테이블이 &lt;b&gt;Index lookup&lt;/b&gt;을 수행해 필터링 비용이 크게 줄었음 (cost=500, 실제 5ms)&lt;/li&gt;
&lt;li data-end=&quot;1028&quot; data-start=&quot;946&quot;&gt;customers 테이블도 인덱스 조회를 하고, orders에서 필터링된 190건에 대해서만 190번 루프 실행 (loops=190)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1751559263355&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                             |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -&amp;gt; Nested loop join                                                                                                                                                |
|    -&amp;gt; Index lookup on orders using idx_orders_total_price (total_price &amp;gt; 1000000)  (cost=500 rows=200) (actual time=5.0..5.5 rows=190 loops=1)                      |
|    -&amp;gt; Index lookup on customers using idx_customers_region (region = 'Seoul')  (cost=100 rows=50) (actual time=0.5..0.6 rows=48 loops=190)                          |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.008 sec)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;결과 해석&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-start=&quot;1816&quot; data-end=&quot;1867&quot;&gt;&lt;b&gt;Nested loop join&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;방식 사용&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-start=&quot;1816&quot; data-end=&quot;1867&quot;&gt;MySQL 에서는 조인 알고리즘을 직접 지정하기 어렵다...&lt;/li&gt;
&lt;li data-start=&quot;1816&quot; data-end=&quot;1867&quot;&gt;특정 DBMS 에서는 &lt;b&gt;조인 힌트(Hint)&lt;/b&gt; 를 통해 조인 방식을 명시하거나 유도할 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-start=&quot;1868&quot; data-end=&quot;1931&quot;&gt;orders 테이블에서 total_price &amp;gt; 1000000 조건으로 인덱스를 통해 190개 정도의 행만 필터링해서 가져옴 &amp;rarr; 훨씬 효율적이다.&lt;/li&gt;
&lt;li data-start=&quot;1932&quot; data-end=&quot;2000&quot;&gt;이후 각 190개의 주문 행마다 customers 테이블에서 region='Seoul' 조건으로 인덱스 조회를 수행함.&lt;/li&gt;
&lt;li data-start=&quot;1932&quot; data-end=&quot;2000&quot;&gt;orders 테이블에 인덱스를 활용해 필터링 후 조인하여, 외부 루프가 훨씬 줄어든 덕분에 쿼리가 더 빠르게 실행되었다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>SQL/성능 개선</category>
      <author>가지코딩</author>
      <guid isPermaLink="true">https://gajicoding.tistory.com/396</guid>
      <comments>https://gajicoding.tistory.com/396#entry396comment</comments>
      <pubDate>Fri, 4 Jul 2025 01:15:56 +0900</pubDate>
    </item>
    <item>
      <title>Index Lookup vs Covering Index Lookup</title>
      <link>https://gajicoding.tistory.com/395</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Index Lookup 이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스로 조건에 맞는 row를 찾고, 그 뒤에 실제 테이블(클러스터드 인덱스)에서 필요한 데이터를 가져오는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 예시 쿼리&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751557280466&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT * FROM users WHERE nickname = 'nickname999999';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;* EXPLAIN ANALYZE &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751557315247&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;+-------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                         |
+-------------------------------------------------------------------------------------------------------------------------------------------------+
| -&amp;gt; Index lookup on users using idx_user_nickname (nickname = 'nickname999999')  (cost=0.37 rows=1) (actual time=0.0349..0.0372 rows=1 loops=1)
 |
+-------------------------------------------------------------------------------------------------------------------------------------------------+&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 작동 방식&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;725&quot; data-start=&quot;695&quot;&gt;nickname 인덱스에서 해당 값을 찾는다.&lt;/li&gt;
&lt;li data-end=&quot;752&quot; data-start=&quot;726&quot;&gt;인덱스에는 nickname만 있으므로,&lt;/li&gt;
&lt;li data-end=&quot;790&quot; data-start=&quot;753&quot;&gt;&lt;b&gt;실제 테이블로 이동하여&lt;/b&gt; 나머지 컬럼 데이터를 가져온다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 특징 &lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;826&quot; data-start=&quot;802&quot;&gt;&lt;b&gt;추가적인 테이블 접근 비용&lt;/b&gt;이 존재&lt;/li&gt;
&lt;li data-end=&quot;862&quot; data-start=&quot;827&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;쿼리 대상 컬럼이 인덱스에 없으면 반드시 테이블 접근이 필요&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Covering Index Lookup 이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리에 필요한 모든 컬럼이 인덱스에 포함되어 있어, 테이블을 전혀 읽지 않고 인덱스만으로 결과를 반환하는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;예시 쿼리&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751557406014&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;SELECT nickname FROM users WHERE nickname=&quot;nickname999999&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;* EXPLAIN ANALYZE&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751557425072&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;+--------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                |
+--------------------------------------------------------------------------------------------------------------------------------------------------------+
| -&amp;gt; Covering index lookup on users using idx_user_nickname (nickname = 'nickname999999')  (cost=1.1 rows=1) (actual time=0.012..0.0135 rows=1 loops=1)
 |
+--------------------------------------------------------------------------------------------------------------------------------------------------------+&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;작동 방식&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1309&quot; data-start=&quot;1267&quot;&gt;인덱스에서 nickname = 'nickname999999'을 찾음&lt;/li&gt;
&lt;li data-end=&quot;1339&quot; data-start=&quot;1310&quot;&gt;&lt;b&gt;쿼리에 필요한 정보가 인덱스에 모두 존재&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1370&quot; data-start=&quot;1340&quot;&gt;테이블로 접근하지 않고, 인덱스만으로 결과를 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 특징&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1410&quot; data-start=&quot;1381&quot;&gt;&lt;b&gt;테이블 접근 생략&lt;/b&gt; &amp;rarr; 디스크 I/O 최소화&lt;/li&gt;
&lt;li data-end=&quot;1446&quot; data-start=&quot;1411&quot;&gt;빠른 응답속도, 특히 &lt;b&gt;읽기 많은 서비스에서 매우 유리&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1484&quot; data-start=&quot;1447&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;조건 + SELECT 대상 컬럼이 인덱스에 모두 포함되어야 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Index Lookup vs Covering Index Lookup&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt; 구분 &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; Index Lookup &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; Covering Index Lookup &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;테이블 접근&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;인덱스 + 테이블 row&lt;/td&gt;
&lt;td&gt;인덱스만 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;속도&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;보통&lt;/td&gt;
&lt;td&gt;더 빠름 (I/O 적음)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;인덱스에 필요한 컬럼&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;일부만 있어도 됨&lt;/td&gt;
&lt;td&gt;쿼리 사용 컬럼 &lt;b&gt;전부&lt;/b&gt; 포함되어야 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;활용 조건&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;WHERE에만 인덱스 컬럼 사용&lt;/td&gt;
&lt;td&gt;WHERE + SELECT 컬럼 &lt;b&gt;모두 인덱스 포함&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;테이블 접근 필요 &amp;rarr; I/O 증가&lt;/td&gt;
&lt;td&gt;인덱스에 모든 컬럼을 포함해야 함 &amp;rarr; 인덱스 크기 증가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;실전 예시&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;SELECT *&lt;/td&gt;
&lt;td&gt;SELECT nickname&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2201&quot; data-start=&quot;2156&quot;&gt;Index Lookup: 일반적인 인덱스 조회 &amp;rarr; 테이블 row 접근 필요&lt;/li&gt;
&lt;li data-end=&quot;2248&quot; data-start=&quot;2202&quot;&gt;Covering Index Lookup: 인덱스만으로 완전 처리 &amp;rarr; 더 빠름&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Covering Index 사용 판단 기준&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt; 상황 &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; Covering Index 적합도 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;읽기 비중이 높고, 자주 조회되는 컬럼만 조회&lt;/td&gt;
&lt;td&gt;✅ 매우 적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;쓰기(UPDATE/INSERT) 비중이 높고, 자주 수정되는 컬럼 포함&lt;/td&gt;
&lt;td&gt;❌ 부적합 (오히려 성능 저하 우려)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;자주 쓰이는 컬럼이 제한적이고, 쓰기 비중도 적당한 경우&lt;/td&gt;
&lt;td&gt;⚠️ 선택적으로 사용 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt; 실무 팁&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;125&quot; data-start=&quot;55&quot;&gt;&lt;b&gt;SELECT *는 피해야 한다&lt;/b&gt;&lt;br /&gt;필요한 컬럼만 명시적으로 선택하여 커버링 인덱스를 활용할 수 있도록 한다.&lt;/li&gt;
&lt;li data-end=&quot;230&quot; data-start=&quot;127&quot;&gt;&lt;b&gt;커버링 인덱스 설계 전에 조회 패턴을 분석해야 한다&lt;/b&gt;&lt;br /&gt;자주 사용하는 WHERE 조건과 SELECT 컬럼 조합을 파악하여 인덱스에 포함할 컬럼을 신중하게 결정한다.&lt;/li&gt;
&lt;li data-end=&quot;349&quot; data-start=&quot;232&quot;&gt;&lt;b&gt;쓰기 작업이 많은 테이블은 인덱스 크기와 갱신 비용을 반드시 고려해야 한다&lt;/b&gt;&lt;br /&gt;커버링 인덱스는 인덱스 크기를 키우고 쓰기 시 인덱스 갱신 비용을 증가시켜 무분별한 인덱스 추가는 피하는 것이 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;* 읽기 성능 vs 쓰기 비용 균형 &lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;읽기 성능 향상은 쓰기 비용 증가로 이어지는 상호 연관 관계가 있다&lt;/li&gt;
&lt;li&gt;읽기 중심 시스템에서는 커버링 인덱스가 매우 효과적이나, 쓰기 중심 시스템에서는 오히려 성능 저하를 유발할 수 있으므로 신중히 설계할 것&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>SQL/성능 개선</category>
      <author>가지코딩</author>
      <guid isPermaLink="true">https://gajicoding.tistory.com/395</guid>
      <comments>https://gajicoding.tistory.com/395#entry395comment</comments>
      <pubDate>Fri, 4 Jul 2025 00:58:28 +0900</pubDate>
    </item>
    <item>
      <title>[  Docker + CI/CD] 4. AWS 연동</title>
      <link>https://gajicoding.tistory.com/393</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  목차&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t1&quot;&gt;AWS 회원 가입 &amp;amp; 로그인&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t2&quot;&gt;EC2 설정&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t3&quot;&gt;서버 설정&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t4&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;github repository 설정&lt;/span&gt; &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t5&quot;&gt;프로젝트 설정 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t6&quot;&gt;EC2 인바운드 규칙 설정&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t7&quot;&gt;배포 확인&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t8&quot;&gt;인스턴스 중지&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t1&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;1. AWS&amp;nbsp;회원&amp;nbsp;가입&amp;nbsp;&amp;amp;&amp;nbsp;로그인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://portal.aws.amazon.com/billing/signup&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://portal.aws.amazon.com/billing/signup&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1751469685736&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;AWS Console - Signup&quot; data-og-description=&quot;&quot; data-og-host=&quot;portal.aws.amazon.com&quot; data-og-source-url=&quot;https://portal.aws.amazon.com/billing/signup&quot; data-og-url=&quot;https://portal.aws.amazon.com/billing/signup&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://portal.aws.amazon.com/billing/signup&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://portal.aws.amazon.com/billing/signup&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;AWS Console - Signup&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;portal.aws.amazon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이메일 인증, 주소 등록, 카드 등록, 본인 인증 등의 절차를 거쳐 회원가입을 진행한&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;로그인 한다 !&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t2&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;2. EC2&amp;nbsp;설정&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;왼쪽 위 검색창에서 ec2 를 입력 후 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;515&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRzkWN/btsO2rucpgl/xLqFQvGJG6GGMOAmSLbfC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRzkWN/btsO2rucpgl/xLqFQvGJG6GGMOAmSLbfC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRzkWN/btsO2rucpgl/xLqFQvGJG6GGMOAmSLbfC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRzkWN%2FbtsO2rucpgl%2FxLqFQvGJG6GGMOAmSLbfC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;515&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;515&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7z8Ve/btsO0OXV0KM/9LL2Ym4aP9TuvYhKIqaWDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7z8Ve/btsO0OXV0KM/9LL2Ym4aP9TuvYhKIqaWDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7z8Ve/btsO0OXV0KM/9LL2Ym4aP9TuvYhKIqaWDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7z8Ve%2FbtsO0OXV0KM%2F9LL2Ym4aP9TuvYhKIqaWDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;230&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;230&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ec2 화면에서 &amp;lsquo;인스턴스&amp;rsquo; 를 선택&lt;/p&gt;
&lt;div&gt;
&lt;style&gt;  #article-view img {  	border: 2px solid #000;  }  #tinymce img {  	border: 2px solid #000;  }&lt;\/style&gt;&lt;\/div&gt;&quot;&lt;span id=&quot;mce_marker&quot; data-mce-type=&quot;bookmark&quot;&gt;&amp;#xFEFF;&amp;#x200B;&lt;/span&gt;&lt;/style&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;305&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Kzl71/btsO2xVfCgK/vgziq3bGWpRKXV5ROBMP3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Kzl71/btsO2xVfCgK/vgziq3bGWpRKXV5ROBMP3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Kzl71/btsO2xVfCgK/vgziq3bGWpRKXV5ROBMP3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKzl71%2FbtsO2xVfCgK%2Fvgziq3bGWpRKXV5ROBMP3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;305&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;305&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;인스턴스 시작 버튼 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UUinz/btsO1ikMoRW/ZK4brBv9lkPmql6Mx80qj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UUinz/btsO1ikMoRW/ZK4brBv9lkPmql6Mx80qj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UUinz/btsO1ikMoRW/ZK4brBv9lkPmql6Mx80qj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUUinz%2FbtsO1ikMoRW%2FZK4brBv9lkPmql6Mx80qj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;442&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;인스턴스 생성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이름 : my-server&lt;/li&gt;
&lt;li&gt;Amazon Machine Image(AMI) : Amazon Linux 2023 AMI&lt;/li&gt;
&lt;li&gt;인스턴스 유형 : t2.micro&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;1575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/56TZH/btsO29MSLGw/NDK63FVbKY37Js3r93ep2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/56TZH/btsO29MSLGw/NDK63FVbKY37Js3r93ep2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/56TZH/btsO29MSLGw/NDK63FVbKY37Js3r93ep2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F56TZH%2FbtsO29MSLGw%2FNDK63FVbKY37Js3r93ep2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;1575&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;1575&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;키 페어 생성&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lsquo;새 키 페어 생성&amp;rsquo; 클릭
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;키 페어 이름 : my-server-keypair&lt;/li&gt;
&lt;li&gt;키 페어 유형: RSA&lt;/li&gt;
&lt;li&gt;프라이빗 키 파일 형식: pem&lt;/li&gt;
&lt;li&gt;&amp;lsquo;키 페어 생성&amp;rsquo; 후 다운로드&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1011&quot; data-origin-height=&quot;317&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxxV4q/btsO2n6qOPn/BIbD9U169Lv3bzsKVeLBIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxxV4q/btsO2n6qOPn/BIbD9U169Lv3bzsKVeLBIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxxV4q/btsO2n6qOPn/BIbD9U169Lv3bzsKVeLBIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxxV4q%2FbtsO2n6qOPn%2FBIbD9U169Lv3bzsKVeLBIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;612&quot; height=&quot;317&quot; data-origin-width=&quot;1011&quot; data-origin-height=&quot;317&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;737&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NLqto/btsO2HQQs3S/2SyfmewWtyKYOPLfuvvbck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NLqto/btsO2HQQs3S/2SyfmewWtyKYOPLfuvvbck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NLqto/btsO2HQQs3S/2SyfmewWtyKYOPLfuvvbck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNLqto%2FbtsO2HQQs3S%2F2SyfmewWtyKYOPLfuvvbck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;501&quot; height=&quot;497&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;737&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;네트워크 설정&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;보안 그룹 생성 선택&lt;/li&gt;
&lt;li&gt;Allow SSH traffic from , &amp;lsquo;위치 무관&amp;rsquo; 선택&lt;/li&gt;
&lt;li&gt;인터넷에서 HTTPS 트래픽 허용 선택&lt;/li&gt;
&lt;li&gt;인터넷에서 HTTP 트래픽 허용 선택&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1003&quot; data-origin-height=&quot;833&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCmNS7/btsO3cpjaPH/IzUPmGbu7TaxAiGJMaIfwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCmNS7/btsO3cpjaPH/IzUPmGbu7TaxAiGJMaIfwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCmNS7/btsO3cpjaPH/IzUPmGbu7TaxAiGJMaIfwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCmNS7%2FbtsO3cpjaPH%2FIzUPmGbu7TaxAiGJMaIfwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;556&quot; height=&quot;462&quot; data-origin-width=&quot;1003&quot; data-origin-height=&quot;833&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;인스턴스 시작 클릭 !&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t3&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;3. 서버&amp;nbsp;설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; ec2 접속 &lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;.pem 파일 권한 설정 (chmod 400)&lt;/li&gt;
&lt;li&gt;ssh 로 접속&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1751469635678&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssh -i test-server.pem ec2-user@{퍼블릭 IPv4 DNS 주소}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ec2 에 jdk 설치&lt;/p&gt;
&lt;pre id=&quot;code_1751469767771&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo yum install -y java-17-amazon-corretto-devel&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ec2에 docker, docker-compose 설치&lt;/p&gt;
&lt;pre id=&quot;code_1751469774307&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo yum install docker

# Docker 서비스 시작
sudo systemctl start docker

# Docker 서비스 작동 상태 확인
sudo systemctl status docker

# Docker 서비스를 운영체제 부팅시 자동 시작하도록 설정
sudo systemctl enable docker

# docker 명령어를 sudo 없이 사용하기 위해 계정을 docker 그룹에 소속 (계정 재접속 필요)
sudo usermod -aG docker ec2-user

# docker-compose 설치
sudo curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose

# 권한 부여
sudo chmod +x /usr/local/bin/docker-compose&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t4&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;4. &lt;span data-token-index=&quot;0&quot;&gt;github repository 설정&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 repository 생성 !&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Settings &amp;gt; Secrets and variables &amp;gt; Actions 로 이동&lt;/li&gt;
&lt;li&gt;Repository secrets 에 key 추가
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;SSH_KEY: .pem 파일 내용 복사&lt;/li&gt;
&lt;li&gt;SSH_HOST: 퍼블릭 IPv4 DNS 주소 복사&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;925&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XAmkr/btsO2azvjXh/Lu1KMPKd106IrPjkvMWVVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XAmkr/btsO2azvjXh/Lu1KMPKd106IrPjkvMWVVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XAmkr/btsO2azvjXh/Lu1KMPKd106IrPjkvMWVVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXAmkr%2FbtsO2azvjXh%2FLu1KMPKd106IrPjkvMWVVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;926&quot; height=&quot;925&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;925&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mOSQW/btsO0648njI/mkT4jl475G6HQMdsych8fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mOSQW/btsO0648njI/mkT4jl475G6HQMdsych8fk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mOSQW/btsO0648njI/mkT4jl475G6HQMdsych8fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmOSQW%2FbtsO0648njI%2FmkT4jl475G6HQMdsych8fk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;509&quot; height=&quot;343&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;795&quot; data-origin-height=&quot;429&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XTRNN/btsO2alX5Kx/G1i5PIWHupG8kuwEthmUh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XTRNN/btsO2alX5Kx/G1i5PIWHupG8kuwEthmUh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XTRNN/btsO2alX5Kx/G1i5PIWHupG8kuwEthmUh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXTRNN%2FbtsO2alX5Kx%2FG1i5PIWHupG8kuwEthmUh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;510&quot; height=&quot;275&quot; data-origin-width=&quot;795&quot; data-origin-height=&quot;429&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t5&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;5. 프로젝트 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/gajicoding/cicd-aws/tree/11f4a77a9db8cb029a07f870be1f329dcd704a6d&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/gajicoding/cicd-aws/tree/11f4a77a9db8cb029a07f870be1f329dcd704a6d&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1751506374210&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - gajicoding/cicd-aws&quot; data-og-description=&quot;Contribute to gajicoding/cicd-aws development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/gajicoding/cicd-aws/tree/11f4a77a9db8cb029a07f870be1f329dcd704a6d&quot; data-og-url=&quot;https://github.com/gajicoding/cicd-aws&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://github.com/gajicoding/cicd-aws/tree/11f4a77a9db8cb029a07f870be1f329dcd704a6d&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/gajicoding/cicd-aws/tree/11f4a77a9db8cb029a07f870be1f329dcd704a6d&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - gajicoding/cicd-aws&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to gajicoding/cicd-aws development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.github/workflows/deploy.yml 파일 추가&lt;/p&gt;
&lt;pre id=&quot;code_1751471427179&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name: Deploy

on:
  workflow_dispatch:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'adopt'
          
      - name: Grant execute permission for gradlew
        run: chmod +x ./gradlew
        
      - name: gradlew bootJar
        run: ./gradlew bootJar
        
      - name: copy jar to server
        uses: appleboy/scp-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ec2-user
          key: ${{ secrets.SSH_KEY }}
          port: 22
          source: &quot;./build/libs/*.jar&quot;
          target: &quot;~&quot;
          strip_components: 2

      - name: SSH Commands
        uses: appleboy/ssh-action@v0.1.6
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ec2-user
          key: ${{ secrets.SSH_KEY }}
          port: 22
          script_stop: true
          script: |
            sudo yum update -y &amp;amp;&amp;amp; sudo yum install -y java-21-amazon-corretto
            for pid in $(pgrep java); do
              if ps -p $pid -o args= | grep -q 'java -jar'; then
                echo &quot;Java process with 'java -jar' found (PID: $pid). Terminating...&quot;
                kill -9 $pid
              fi
            done
            echo &quot;nohup java -jar ~/*.jar &amp;gt; ~/app.log 2&amp;gt;&amp;amp;1 &amp;amp;&quot; | at now&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;* 작업 순서&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 checkout&lt;/li&gt;
&lt;li&gt;JDK 설정&lt;/li&gt;
&lt;li&gt;gradlew 실행&lt;/li&gt;
&lt;li&gt;jar 파일 전송&lt;/li&gt;
&lt;li&gt;jar 파일 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;596&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ozldH/btsO198uNNz/X7x5f46BAAbyauyTCxH49K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ozldH/btsO198uNNz/X7x5f46BAAbyauyTCxH49K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ozldH/btsO198uNNz/X7x5f46BAAbyauyTCxH49K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FozldH%2FbtsO198uNNz%2FX7x5f46BAAbyauyTCxH49K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;596&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;596&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t6&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;6. EC2 인바운드 규칙 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인스턴스 &amp;gt; 보안 &amp;gt; 보안 그룹 &amp;gt; 인바운드 규칙 편집&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0RjZ9/btsO2mTFVVf/CXZQzc8c1nt4e7JHXrix80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0RjZ9/btsO2mTFVVf/CXZQzc8c1nt4e7JHXrix80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0RjZ9/btsO2mTFVVf/CXZQzc8c1nt4e7JHXrix80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0RjZ9%2FbtsO2mTFVVf%2FCXZQzc8c1nt4e7JHXrix80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;911&quot; height=&quot;322&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;322&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t7&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;7. 배포 확인&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;709&quot; data-origin-height=&quot;113&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KPJkz/btsO3duZuU1/C2gAMtLPfX4K7uF4tH68yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KPJkz/btsO3duZuU1/C2gAMtLPfX4K7uF4tH68yk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KPJkz/btsO3duZuU1/C2gAMtLPfX4K7uF4tH68yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKPJkz%2FbtsO3duZuU1%2FC2gAMtLPfX4K7uF4tH68yk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;709&quot; height=&quot;113&quot; data-origin-width=&quot;709&quot; data-origin-height=&quot;113&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t8&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;8. 인스턴스 중지&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot; data-token-index=&quot;0&quot;&gt; AWS를 사용하는 것은 비용이 발생할 수 있으니 주의해야 한다 !!!&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;사용하지 않는 EC2 서버는 &amp;lsquo;중지&amp;rsquo; 상태로 변경. 서버를 없애려면 위해선 &amp;lsquo;종료&amp;rsquo; 선택&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1641&quot; data-origin-height=&quot;359&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NaedW/btsO2Hi3vgy/nqN4eMkxhPr0YirbocdYyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NaedW/btsO2Hi3vgy/nqN4eMkxhPr0YirbocdYyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NaedW/btsO2Hi3vgy/nqN4eMkxhPr0YirbocdYyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNaedW%2FbtsO2Hi3vgy%2FnqN4eMkxhPr0YirbocdYyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1641&quot; height=&quot;359&quot; data-origin-width=&quot;1641&quot; data-origin-height=&quot;359&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;번외&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;docker 로 배포하기 !&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;강의에서 ec2 에 docker 환경 설정은 해주었지만, docker 를 사용하여 배포하지는 않았다...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;궁금한건 직접 해보기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;a href=&quot;https://github.com/gajicoding/cicd-aws/tree/cad62e7622af1fae48c4a3db98017bdae13f2339&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/gajicoding/cicd-aws/tree/cad62e7622af1fae48c4a3db98017bdae13f2339&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1751510830182&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - gajicoding/cicd-aws&quot; data-og-description=&quot;Contribute to gajicoding/cicd-aws development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/gajicoding/cicd-aws/tree/cad62e7622af1fae48c4a3db98017bdae13f2339&quot; data-og-url=&quot;https://github.com/gajicoding/cicd-aws&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/mycWl/hyZf8RE40a/C7T4kvttPdJ9GXP30OBem1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/rliuL/hyZfWp9Q4N/h5HYzT1KNQs7PBhmMtE7YK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/gajicoding/cicd-aws/tree/cad62e7622af1fae48c4a3db98017bdae13f2339&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/gajicoding/cicd-aws/tree/cad62e7622af1fae48c4a3db98017bdae13f2339&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/mycWl/hyZf8RE40a/C7T4kvttPdJ9GXP30OBem1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/rliuL/hyZfWp9Q4N/h5HYzT1KNQs7PBhmMtE7YK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - gajicoding/cicd-aws&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to gajicoding/cicd-aws development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/강의</category>
      <author>가지코딩</author>
      <guid isPermaLink="true">https://gajicoding.tistory.com/393</guid>
      <comments>https://gajicoding.tistory.com/393#entry393comment</comments>
      <pubDate>Thu, 3 Jul 2025 01:01:58 +0900</pubDate>
    </item>
    <item>
      <title>Windows에서 EC2 SSH 접속 시 WARNING: UNPROTECTED PRIVATE KEY FILE! 오류 해결</title>
      <link>https://gajicoding.tistory.com/392</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 상황&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS EC2 인스턴스에 SSH로 접속을 시도했는데 다음과 같은 오류가 발생했다:&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0S0GK/btsO1gmXYnY/zb9Ahh96PSkR4sEAUFkCp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0S0GK/btsO1gmXYnY/zb9Ahh96PSkR4sEAUFkCp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0S0GK/btsO1gmXYnY/zb9Ahh96PSkR4sEAUFkCp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0S0GK%2FbtsO1gmXYnY%2Fzb9Ahh96PSkR4sEAUFkCp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1088&quot; height=&quot;208&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;208&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1751467729713&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions for 'test-server.pem' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key &quot;test-server.pem&quot;: bad permissions
ec2-user@ec2-xx-xxx-xx-xxx.compute.amazonaws.com: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인 분석&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt; 이 오류는 SSH에서 사용하는 &lt;b&gt;비공개 키(.pem)&lt;/b&gt; 파일의 &lt;b&gt;권한이 너무 널널할 때&lt;/b&gt; 발생한다.&lt;/span&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리눅스 기반에서는 chmod 400으로 쉽게 해결되지만, &lt;b&gt;Windows에서는 NTFS 파일 시스템을 사용하기 때문에 chmod가 제대로 적용되지 않는다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;특히, Windows의 기본 사용자 그룹인 BUILTIN\Users, Everyone, Administrators가 해당 파일에 접근 권한을 가지고 있으면 SSH 접속이 거부된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ffc1c8;&quot;&gt;&lt;i&gt; SSH로 접속 시도 전, git bash 로 권한 설정을 해주었지만, 적용되지 않은 듯 하다&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;53&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQWrqh/btsO2ehCTwV/ZjnH8LG0QqK7PG0LgNrr50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQWrqh/btsO2ehCTwV/ZjnH8LG0QqK7PG0LgNrr50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQWrqh/btsO2ehCTwV/ZjnH8LG0QqK7PG0LgNrr50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQWrqh%2FbtsO2ehCTwV%2FZjnH8LG0QqK7PG0LgNrr50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;505&quot; height=&quot;53&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;53&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;84&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I5NFr/btsO2WUzvBD/vV3jbWw23uonlhyG7iyIqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I5NFr/btsO2WUzvBD/vV3jbWw23uonlhyG7iyIqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I5NFr/btsO2WUzvBD/vV3jbWw23uonlhyG7iyIqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI5NFr%2FbtsO2WUzvBD%2FvV3jbWw23uonlhyG7iyIqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;599&quot; height=&quot;84&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;84&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt; Windows PowerShell로 파일 권한 재설정 &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1060&quot; data-start=&quot;1028&quot;&gt;PowerShell 실행 (관리자 권한 권장)&lt;/li&gt;
&lt;li data-end=&quot;1060&quot; data-start=&quot;1028&quot;&gt;아래 스크립트 실행&lt;/li&gt;
&lt;li data-end=&quot;1060&quot; data-start=&quot;1028&quot;&gt;(본인의 .pem 경로에 맞게 수정)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1751468140204&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$PemPath = &quot;C:\Users\KNG\Desktop\key\aws_key\test-server.pem&quot;

# 1. 상속 제거
icacls $PemPath /inheritance:r

# 2. 불필요한 사용자 권한 제거
icacls $PemPath /remove:g &quot;Users&quot;
icacls $PemPath /remove:g &quot;Administrators&quot;
icacls $PemPath /remove &quot;NT AUTHORITY\SYSTEM&quot;
icacls $PemPath /remove &quot;BUILTIN\Users&quot;
icacls $PemPath /remove &quot;BUILTIN\Administrators&quot;

# 3. 현재 사용자에게만 읽기 권한 부여
$CurrentUser = &quot;$env:USERNAME&quot;
icacls $PemPath /grant:r &quot;$CurrentUser:R&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 적용 여부 확인&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751468208350&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;icacls &quot;C:\Users\KNG\Desktop\key\aws_key\test-server.pem&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;43&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WMEfo/btsO1fuSDOi/f6IhQ5VHdf3n6Jgla3Znt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WMEfo/btsO1fuSDOi/f6IhQ5VHdf3n6Jgla3Znt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WMEfo/btsO1fuSDOi/f6IhQ5VHdf3n6Jgla3Znt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWMEfo%2FbtsO1fuSDOi%2Ff6IhQ5VHdf3n6Jgla3Znt0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;868&quot; height=&quot;43&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;43&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;현재 사용자: (R)&lt;/b&gt; 만 남아있다면 성공 !&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSH 접속도 잘 되는 것을 확인했다 !&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1094&quot; data-origin-height=&quot;230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmF2vf/btsO2waXjmv/OkDZwHZukH0XtRmJJ2w3W0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmF2vf/btsO2waXjmv/OkDZwHZukH0XtRmJJ2w3W0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmF2vf/btsO2waXjmv/OkDZwHZukH0XtRmJJ2w3W0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmF2vf%2FbtsO2waXjmv%2FOkDZwHZukH0XtRmJJ2w3W0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1094&quot; height=&quot;230&quot; data-origin-width=&quot;1094&quot; data-origin-height=&quot;230&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Backend/문제 해결 (Troubleshooting)</category>
      <author>가지코딩</author>
      <guid isPermaLink="true">https://gajicoding.tistory.com/392</guid>
      <comments>https://gajicoding.tistory.com/392#entry392comment</comments>
      <pubDate>Thu, 3 Jul 2025 00:02:33 +0900</pubDate>
    </item>
    <item>
      <title>[  Docker + CI/CD] 3-2. Docker 모니터링과 로깅﻿</title>
      <link>https://gajicoding.tistory.com/391</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  목차&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t1&quot;&gt;Docker 모니터링&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t2&quot;&gt;Container 리소스 모니터링&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t3&quot;&gt;Container 로깅&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  학습 목표&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Docker 컨테이너를 모니터링하고 로깅을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t1&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;1. Docker&amp;nbsp;모니터링&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Docker 모니터링&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨테이너가 현재 어떤 상태로 실행되고 있는지 확인하는 활동&lt;/li&gt;
&lt;li&gt;확인 가능한 정보
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-start=&quot;362&quot; data-end=&quot;371&quot;&gt;CPU 사용량&lt;/li&gt;
&lt;li data-start=&quot;372&quot; data-end=&quot;381&quot;&gt;메모리 사용량&lt;/li&gt;
&lt;li data-start=&quot;382&quot; data-end=&quot;392&quot;&gt;네트워크 트래픽&lt;/li&gt;
&lt;li data-start=&quot;393&quot; data-end=&quot;402&quot;&gt;디스크 I/O&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-start=&quot;393&quot; data-end=&quot;402&quot;&gt;모니터링의 중요성
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-start=&quot;420&quot; data-end=&quot;434&quot;&gt;과도한 자원 사용 감지&lt;/li&gt;
&lt;li data-start=&quot;435&quot; data-end=&quot;445&quot;&gt;병목 현상 파악&lt;/li&gt;
&lt;li data-start=&quot;446&quot; data-end=&quot;464&quot;&gt;시스템 최적화 및 안정성 확보&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t2&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;2. Container 리소스 모니터링&lt;/h2&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;docker stats&lt;/span&gt;: 컨테이너 실시간 모니터링&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751464758924&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker stats&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;596&quot; data-start=&quot;561&quot;&gt;실행 중인 모든 컨테이너의 자원 사용 현황을 실시간으로 표시&lt;/li&gt;
&lt;li data-end=&quot;657&quot; data-start=&quot;597&quot;&gt;출력 정보:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;657&quot; data-start=&quot;608&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;615&quot; data-start=&quot;608&quot;&gt;CPU %&lt;/li&gt;
&lt;li data-end=&quot;632&quot; data-start=&quot;618&quot;&gt;메모리 사용량 / 제한&lt;/li&gt;
&lt;li data-end=&quot;645&quot; data-start=&quot;635&quot;&gt;네트워크 I/O&lt;/li&gt;
&lt;li data-end=&quot;657&quot; data-start=&quot;648&quot;&gt;디스크 I/O&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;* 특정 컨테이너만 보고 싶다면?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751464758925&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker stats &amp;lt;컨테이너 이름 또는 ID&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;htop&lt;/span&gt;: 시스템 리소스 모니터링&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리눅스 시스템 전체의 자원 사용 상태를 직관적으로 보여주는 CLI 도구&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1751464836939&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run --name test-tools -ti -d ubuntu:22.04
docker exec -ti test-tools /bin/bash
apt update &amp;amp;&amp;amp; apt install -y htop
htop&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt; df와 du&lt;/span&gt;: 디스크 사용량 확인&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;df: 시스템 전체 디스크 사용량&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1751464880853&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker exec -ti test-tools /bin/bash
df -h
exit&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;du: 디렉토리별 디스크 사용량&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1751464885940&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker exec -ti test-tools /bin/bash
du -sh              # 현재 디렉토리 총 사용량
du -h --max-depth=1 # 하위 디렉토리 1단계까지 사용량
exit&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t3&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;3. Container&amp;nbsp;로깅&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 기본 로깅 방식&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Docker는 모든 컨테이너의 stdout / stderr 출력을 캡처하여, 기본적으로 &lt;b&gt;json-file&lt;/b&gt; 로깅 드라이버를 사용해 로그 파일에 기록한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1751464932455&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/var/lib/docker/containers/&amp;lt;컨테이너ID&amp;gt;/&amp;lt;컨테이너ID&amp;gt;-json.log&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 로그 확인 예제&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751464950829&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run --name logs-test --rm -d ubuntu:22.04 /bin/bash -c 'while true; do date; sleep 1; done'
docker logs logs-test                   # 전체 출력
docker logs -f logs-test                # 실시간 로그 보기
docker logs -f --tail 10 logs-test      # 마지막 10줄부터 보기&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1751464957215&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 로그 파일 경로 확인:
docker inspect logs-test --format &quot;{{.LogPath}}&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 로그 파일 관리: 로테이션 설정 &lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 운영 환경에서는 로그가 계속 쌓이기 때문에, &lt;b&gt;크기 제한 및 파일 개수 제한&lt;/b&gt;이 필요하다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-end=&quot;2226&quot; data-start=&quot;2197&quot;&gt;max-size: 개별 로그 파일의 최대 크기&lt;/li&gt;
&lt;li data-end=&quot;2253&quot; data-start=&quot;2227&quot;&gt;max-file: 보관할 최대 파일 개수&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1751465000671&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run -d \
  --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=10 \
  --name nginxtest \
  --restart always \
  -p 80:80 -p 443:443 \
  nginx:latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/강의</category>
      <author>가지코딩</author>
      <guid isPermaLink="true">https://gajicoding.tistory.com/391</guid>
      <comments>https://gajicoding.tistory.com/391#entry391comment</comments>
      <pubDate>Wed, 2 Jul 2025 23:08:32 +0900</pubDate>
    </item>
    <item>
      <title>[  Docker + CI/CD] 3-1. Dockerfile 과 Docker Compose</title>
      <link>https://gajicoding.tistory.com/390</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  목차&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t1&quot;&gt;Dockerfile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t2&quot;&gt;Docker Compose&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t3&quot;&gt;Docker Compose 실습&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  학습 목표&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dockerfile 을 활용하여 나만의 이미지를 만들 수 있습니다.&lt;/li&gt;
&lt;li&gt;Docker Compose 를 활용하여 여러 개의 컨테이너를 관리할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t1&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;1. Dockerfile&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; Dockerfile&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Docker 이미지 생성을 위한 빌드 명세 파일&lt;/li&gt;
&lt;li&gt;텍스트 형식의 스크립트 파일로, 도커가 이미지를 생성할 때 따라야 하는 &lt;b&gt;지시어(instruction)&lt;/b&gt;들을 포함한다.&lt;/li&gt;
&lt;li data-end=&quot;281&quot; data-start=&quot;219&quot;&gt;Dockerfile은 사용자가 정의한 명령어들을 기반으로 &lt;b&gt;새로운 도커 이미지&lt;/b&gt;를 생성하는 데 사용된다.&lt;/li&gt;
&lt;li data-end=&quot;328&quot; data-start=&quot;282&quot;&gt;해당 명령어들은 계층(layer) 단위로 실행되며, 각 계층은 캐시될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 주요 특징 &lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;410&quot; data-start=&quot;348&quot;&gt;비결정적 상태 방지: 명령어 순서와 내용을 명확하게 기술하여 빌드 과정의 재현성을 보장한다.&lt;/li&gt;
&lt;li data-end=&quot;488&quot; data-start=&quot;412&quot;&gt;Immutable Image 정의: 컨테이너는 Dockerfile로 정의된 이미지로부터 동일한 상태로 실행 가능하다.&lt;/li&gt;
&lt;li data-end=&quot;563&quot; data-start=&quot;490&quot;&gt;Layered Filesystem: 각 명령어는 파일시스템 레이어를 생성하며, 변경된 레이어만 캐시 무효화된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 주요 명령어 (Instruction) &lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt; 명령어 &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; 설명 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FROM&lt;/td&gt;
&lt;td&gt;베이스 이미지 지정. 모든 Dockerfile은 이 명령어로 시작해야 함.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RUN&lt;/td&gt;
&lt;td&gt;빌드 시 실행할 명령어. 예: 패키지 설치, 빌드 작업 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CMD&lt;/td&gt;
&lt;td&gt;컨테이너가 실행될 때 기본으로 실행될 명령 지정.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;COPY&lt;/td&gt;
&lt;td&gt;호스트 시스템의 파일을 이미지에 복사.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ADD&lt;/td&gt;
&lt;td&gt;COPY와 유사하나 URL 및 압축 해제 지원.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WORKDIR&lt;/td&gt;
&lt;td&gt;이후 명령어가 실행될 작업 디렉토리 설정.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ENV&lt;/td&gt;
&lt;td&gt;환경 변수 설정.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EXPOSE&lt;/td&gt;
&lt;td&gt;컨테이너에서 열 포트를 명시. 네트워크 연결 용도로 사용.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ENTRYPOINT&lt;/td&gt;
&lt;td&gt;실행 파일 및 인자를 고정적으로 설정할 때 사용.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ARG&lt;/td&gt;
&lt;td&gt;빌드 시점에 사용할 변수 정의. ENV와 달리 런타임에서는 무효.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;기본 구조 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751462151689&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 베이스 이미지 선택 (예: node.js 환경)
FROM node:18

# 2. 작업 디렉토리 설정
WORKDIR /app

# 3. 로컬 파일을 이미지에 복사
COPY package.json ./
COPY . .

# 4. 의존성 설치
RUN npm install

# 5. 앱 실행
CMD [&quot;npm&quot;, &quot;start&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 사용 흐름&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oMyxa/btsO1Vikflm/PlkFkn0KO4kXcl2J10mVMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oMyxa/btsO1Vikflm/PlkFkn0KO4kXcl2J10mVMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oMyxa/btsO1Vikflm/PlkFkn0KO4kXcl2J10mVMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoMyxa%2FbtsO1Vikflm%2FPlkFkn0KO4kXcl2J10mVMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;588&quot; height=&quot;458&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1113&quot; data-start=&quot;1095&quot;&gt;Dockerfile 작성&lt;/li&gt;
&lt;li data-end=&quot;1143&quot; data-start=&quot;1114&quot;&gt;docker build 명령어로 이미지 생성&lt;/li&gt;
&lt;li data-end=&quot;1168&quot; data-start=&quot;1144&quot;&gt;생성된 이미지를 바탕으로 컨테이너 실행&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; Dockerfile 예제 &lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 앱을 실행하는 예제&lt;/p&gt;
&lt;pre id=&quot;code_1751462248551&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM python:3.11

RUN pip install pipenv

WORKDIR /app

ADD . /app/

RUN pipenv --python 3.11
RUN pipenv run pip install poetry
RUN pipenv sync
RUN pipenv run pip install certifi

ARG STAGE

RUN sh -c 'echo &quot;STAGE=$STAGE&quot; &amp;gt; .env'
RUN sh -c 'echo &quot;PYTHONPATH=.&quot; &amp;gt;&amp;gt; .env'
RUN chmod +x ./scripts/run.sh
RUN chmod +x ./scripts/run-worker.sh

CMD [&quot;./scripts/run.sh&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;nginx 이미지를 생성하는 예제&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제로는 nginx:latest 이미지를 사용하면 되지만, 예제를 위해 만들어 보았다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1751462286066&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Dockerfile
FROM ubuntu:22.04
MAINTAINER your-name &amp;lt;your-email@example.com&amp;gt;
LABEL purpose=Web Server

# nginx 패키지 설치
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y nginx

# nginx 설정 파일 복사
COPY nginx.conf /etc/nginx/nginx.conf

# Nginx 실행
CMD [&quot;nginx&quot;, &quot;-g&quot;, &quot;daemon off;&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1751462310194&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# nginx.conf
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] &quot;$request&quot; '
                      '$status $body_bytes_sent &quot;$http_referer&quot; '
                      '&quot;$http_user_agent&quot; &quot;$http_x_forwarded_for&quot;';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    gzip  on;
    gzip_disable &quot;msie6&quot;;

    include /etc/nginx/conf.d/*.conf;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t2&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;2. Docker&amp;nbsp;Compose&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; Docker Compose&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;여러 개의 Docker 컨테이너를 정의하고 동시에 실행&lt;/b&gt;하기 위한 도구&lt;/li&gt;
&lt;li&gt;애플리케이션을 구성하는 여러 서비스(예: 웹 서버, DB 등)를 하나의 설정 파일(docker-compose.yml)에 선언하고,&lt;br /&gt;명령어 하나로 일괄적으로 &lt;b&gt;빌드&lt;/b&gt;, &lt;b&gt;생성&lt;/b&gt;, &lt;b&gt;네트워크 연결&lt;/b&gt;, &lt;b&gt;시작&lt;/b&gt;할 수 있게 해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;핵심 역할&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;268&quot; data-start=&quot;249&quot;&gt;&lt;b&gt;멀티 컨테이너 환경 구성&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;294&quot; data-start=&quot;269&quot;&gt;컨테이너 간의 &lt;b&gt;의존성 정의 및 관리&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;316&quot; data-start=&quot;295&quot;&gt;통합된 &lt;b&gt;빌드/실행/종료 관리&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;343&quot; data-start=&quot;317&quot;&gt;프로젝트별 &lt;b&gt;고립된 네트워크 자동 구성&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 구성 요소&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker-compose.yml 파일
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-end=&quot;668&quot; data-start=&quot;644&quot;&gt;services: 컨테이너 단위 구성&lt;/li&gt;
&lt;li data-end=&quot;691&quot; data-start=&quot;669&quot;&gt;web: 사용자 정의 서비스 이름&lt;/li&gt;
&lt;li data-end=&quot;724&quot; data-start=&quot;692&quot;&gt;depends_on: 실행 순서 정의 (의존 관계)&lt;/li&gt;
&lt;li data-end=&quot;750&quot; data-start=&quot;725&quot;&gt;environment: 환경 변수 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1751462514437&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;version: '3.8'
services:
  web:
    build: .
    ports:
      - &quot;8080:8080&quot;
    depends_on:
      - db
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: example&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 명령어 인터페이스 &lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 139px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt; 명령어 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt; 설명 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;docker-compose up&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;정의된 모든 서비스를 빌드 및 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;docker-compose down&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;실행 중인 모든 서비스 중지 및 네트워크 제거&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;docker-compose build&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;이미지 빌드만 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;docker-compose logs&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;서비스별 로그 출력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;docker-compose ps&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;실행 중인 서비스 목록 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Compose가 내부적으로 하는 일&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1120&quot; data-start=&quot;1079&quot;&gt;docker network를 생성하여 서비스 간 통신 가능하게 함&lt;/li&gt;
&lt;li data-end=&quot;1167&quot; data-start=&quot;1121&quot;&gt;각 서비스에 대해 docker build 또는 docker run 수행&lt;/li&gt;
&lt;li data-end=&quot;1203&quot; data-start=&quot;1168&quot;&gt;컨테이너 이름을 고유하게 지정 (프로젝트_서비스_순번)&lt;/li&gt;
&lt;li data-end=&quot;1265&quot; data-start=&quot;1204&quot;&gt;depends_on, restart, volumes, environment 등의 설정 반영&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Docker Compose 예시&lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring + MySQL 구성 예시&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1671&quot; data-start=&quot;1639&quot;&gt;app은 로컬에서 빌드되는 Spring 애플리케이션&lt;/li&gt;
&lt;li data-end=&quot;1698&quot; data-start=&quot;1672&quot;&gt;mysql은 MySQL 공식 이미지 사용&lt;/li&gt;
&lt;li data-end=&quot;1746&quot; data-start=&quot;1699&quot;&gt;app은 mysql 서비스에 연결 (mysql은 내부 DNS 호스트명)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1751462647421&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;version: '3.8'
services:
  app:
    build: .
    ports:
      - &quot;8080:8080&quot;
    depends_on:
      - mysql
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/testdb

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: testdb
    ports:
      - &quot;3306:3306&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fastapi app&lt;/p&gt;
&lt;pre id=&quot;code_1751462759134&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Dockerfile
FROM python:3.10

RUN pip install pipenv

WORKDIR /app

ADD . /app/

RUN pipenv --python 3.10
RUN pipenv run pip install poetry
RUN pipenv sync
RUN pipenv run pip install certifi


ARG STAGE

RUN sh -c 'echo &quot;STAGE=$STAGE&quot; &amp;gt; .env'
RUN sh -c 'echo &quot;PYTHONPATH=.&quot; &amp;gt;&amp;gt; .env'
# ENV PYTHONPATH=/app
RUN chmod +x ./scripts/run.sh
RUN chmod +x ./scripts/run-worker.sh

# ENV PYTHONPATH=/app
CMD [&quot;./scripts/run.sh&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1751462774812&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# docker-compoe.yaml
version: &quot;3&quot;
services:
  channel-api:
    image: xxxx.dkr.ecr.ap-northeast-2.amazonaws.com/reaction-channel:${AWS_ENV_STAGE:-dev}-${TAG:-latest}
    build:
      context: .
      dockerfile: dockerfile
      args:
        STAGE: ${STAGE:-develop}
    ports:
      - &quot;8000:8000&quot;
    environment:
      - NEW_RELIC_CONFIG_FILE=newrelic.ini
      - NEW_RELIC_ENVIRONMENT=ecs-${STAGE:-develop}
    logging:
      driver: awslogs
      options:
        awslogs-stream-prefix: channel
        awslogs-group: /ecs/reaction/${AWS_ENV_STAGE:-dev}/channel-api
        awslogs-region: ap-northeast-2

  reverseproxy:
    image: xxxxx.dkr.ecr.ap-northeast-2.amazonaws.com/reverseproxy:prod-channel-latest
    ports:
    - &quot;80:80&quot;
    - &quot;81:81&quot;
    logging:
      driver: awslogs
      options:
        awslogs-stream-prefix: reverseproxy
        awslogs-group: /ecs/reaction/${AWS_ENV_STAGE:-dev}/reverseproxy
        awslogs-region: ap-northeast-2&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t3&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;3. Docker&amp;nbsp;Compose&amp;nbsp;실습&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/gajicoding/docker-compose-sample&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/gajicoding/docker-compose-sample&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1751463716668&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - gajicoding/docker-compose-sample&quot; data-og-description=&quot;Contribute to gajicoding/docker-compose-sample development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/gajicoding/docker-compose-sample&quot; data-og-url=&quot;https://github.com/gajicoding/docker-compose-sample&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QmSoM/hyZf7LSwsH/ncAjoRjLlhENRRhBMndAq0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/cgWIxc/hyZfWKlxTC/cbUJJe4IMYXJFkcesXKbl1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/gajicoding/docker-compose-sample&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/gajicoding/docker-compose-sample&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QmSoM/hyZf7LSwsH/ncAjoRjLlhENRRhBMndAq0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/cgWIxc/hyZfWKlxTC/cbUJJe4IMYXJFkcesXKbl1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - gajicoding/docker-compose-sample&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to gajicoding/docker-compose-sample development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; docker-compose 로 실행 &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751463735740&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd ~/docker-compose-sample
docker-compose up -d
docker-compose logs -f&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;redis 연결 테스트&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;http://localhost:8080/test-cache&quot;&gt;http://localhost:8080/test-cache&lt;/a&gt; 에 redis 를 사용한 5초 단위 캐시 적용 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/강의</category>
      <author>가지코딩</author>
      <guid isPermaLink="true">https://gajicoding.tistory.com/390</guid>
      <comments>https://gajicoding.tistory.com/390#entry390comment</comments>
      <pubDate>Wed, 2 Jul 2025 22:46:17 +0900</pubDate>
    </item>
    <item>
      <title>[  Docker + CI/CD] 2. Github actions를 활용한 CI/CD 파이프라인</title>
      <link>https://gajicoding.tistory.com/389</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  목차&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t1&quot;&gt;Github Actions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t2&quot;&gt;Github Actions CI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t3&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;Github Actions CD&lt;/span&gt; &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  학습 목표&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Github Actions를 이해합니다.&lt;/li&gt;
&lt;li&gt;간단한 CI/CD 파이프라인을 구성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t1&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;1. Github&amp;nbsp;Actions&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; GitHub Actions &lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GitHub에서 제공하는 CI/CD 자동화 도구&lt;/li&gt;
&lt;li&gt;코드 변경(pull request, push 등)에 반응하여 &lt;b&gt;자동으로 테스트, 빌드, 배포&lt;/b&gt; 등의 작업을 수행할 수 있다.&lt;/li&gt;
&lt;li&gt;GitHub 저장소 내부에서 실행되며, 외부 서비스 연동 없이 &lt;b&gt;GitHub 환경에서 직접 워크플로우를 구성할 수 있는 점이 특징&lt;/b&gt;이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 주요 구성 요소&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHRxyc/btsO0uxFYr4/tCw4AunIFtSeuSZJ6utXG1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHRxyc/btsO0uxFYr4/tCw4AunIFtSeuSZJ6utXG1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHRxyc/btsO0uxFYr4/tCw4AunIFtSeuSZJ6utXG1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHRxyc%2FbtsO0uxFYr4%2FtCw4AunIFtSeuSZJ6utXG1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;693&quot; height=&quot;872&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Workflow
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;539&quot; data-start=&quot;520&quot;&gt;자동화하고 싶은 전체 작업 정의&lt;/li&gt;
&lt;li data-end=&quot;581&quot; data-start=&quot;540&quot;&gt;.github/workflows/main.yml 등의 형식으로 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;581&quot; data-start=&quot;540&quot;&gt;Job
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;628&quot; data-start=&quot;594&quot;&gt;워크플로우 안에서 병렬 혹은 순차적으로 실행되는 작업 단위&lt;/li&gt;
&lt;li data-end=&quot;659&quot; data-start=&quot;629&quot;&gt;예: build, test, deploy&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;659&quot; data-start=&quot;629&quot;&gt;Step
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;701&quot; data-start=&quot;673&quot;&gt;각 Job 안에서 순차적으로 실행되는 세부 작업&lt;/li&gt;
&lt;li data-end=&quot;736&quot; data-start=&quot;702&quot;&gt;예: 코드 체크아웃, 패키지 설치, 테스트 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;736&quot; data-start=&quot;702&quot;&gt;Action
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;806&quot; data-start=&quot;752&quot;&gt;Step 안에서 재사용 가능한 명령 집합 (GitHub 커뮤니티나 직접 만든 것도 사용 가능)&lt;/li&gt;
&lt;li data-end=&quot;852&quot; data-start=&quot;807&quot;&gt;예: actions/checkout, actions/setup-java&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 트리거 종류&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt; 트리거 &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; 설명 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;push&lt;/td&gt;
&lt;td&gt;특정 브랜치로 푸시될 때 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pull_request&lt;/td&gt;
&lt;td&gt;PR이 열리거나 업데이트될 때 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;schedule&lt;/td&gt;
&lt;td&gt;크론 표현식을 사용해 주기적으로 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;workflow_dispatch&lt;/td&gt;
&lt;td&gt;수동 실행 트리거 (버튼 클릭)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t2&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;2. Github Actions CI&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; GitHub Actions에서 CI&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;test를&amp;nbsp;통과한&amp;nbsp;코드만&amp;nbsp;develop&amp;nbsp;브랜치와&amp;nbsp;main&amp;nbsp;브랜치에&amp;nbsp;merge되도록&amp;nbsp;하여&amp;nbsp;오류를&amp;nbsp;방지하고&amp;nbsp;안정적인&amp;nbsp;코드가&amp;nbsp;배포되고&amp;nbsp;버그를&amp;nbsp;빠르게&amp;nbsp;발견&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Github Actions로 CI 구성하기&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1545&quot; data-start=&quot;1500&quot;&gt;develop, feature/* 브랜치 푸시나 PR 발생 시 실행&lt;/li&gt;
&lt;li data-end=&quot;1588&quot; data-start=&quot;1546&quot;&gt;Java 17 설치 후 ./gradlew test로 테스트 자동 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1751374387755&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name: Run Test

on:
  push:
    branches: [ develop, feature/* ]
  pull_request:
    branches: [ develop ]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: checkout
        uses: actions/checkout@v4

      - name: setup java
        uses: actions/setup-java@v2
        with:
          distribution: 'adopt'
          java-version: '17'

      - name: run test
        run: |
          chmod +x ./gradlew
          ./gradlew clean test&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;실습&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/gajicoding/ci-sample&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/gajicoding/ci-sample&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1751377138907&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - gajicoding/ci-sample&quot; data-og-description=&quot;Contribute to gajicoding/ci-sample development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/gajicoding/ci-sample&quot; data-og-url=&quot;https://github.com/gajicoding/ci-sample&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hlvHD/hyZbpf9vyg/nnKI7QNitfidM4z1Xysfj0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/9noRa/hyZf6eRRpi/Ct3nsGOlhS2tRQeCaxGLPK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/gajicoding/ci-sample&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/gajicoding/ci-sample&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hlvHD/hyZbpf9vyg/nnKI7QNitfidM4z1Xysfj0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/9noRa/hyZf6eRRpi/Ct3nsGOlhS2tRQeCaxGLPK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - gajicoding/ci-sample&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to gajicoding/ci-sample development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t3&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;3. &lt;span data-token-index=&quot;0&quot;&gt;Github Actions CD&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GitHub Actions에서 CD&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배포를 자동화하는 작업을 기술해서 빠르고 간편하게 배포&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 전체 흐름&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;feature/* &amp;rarr; PR 생성 &amp;rarr; gradle test 수행&lt;/li&gt;
&lt;li&gt;test 성공 시 &amp;rarr; 리뷰 &amp;rarr; merge&lt;/li&gt;
&lt;li&gt;main 브랜치로 merge &amp;rarr; 자동 배포&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;627&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JBgK1/btsOZAyIUHZ/OCtuQ4ll4BY684Z8LY174k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JBgK1/btsOZAyIUHZ/OCtuQ4ll4BY684Z8LY174k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JBgK1/btsOZAyIUHZ/OCtuQ4ll4BY684Z8LY174k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJBgK1%2FbtsOZAyIUHZ%2FOCtuQ4ll4BY684Z8LY174k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;565&quot; height=&quot;277&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;627&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;style&gt;  #article-view img {  	border: 2px solid #000;  }  #tinymce img {  	border: 2px solid #000;  }&lt;\/style&gt;&lt;\/div&gt;&quot;&lt;span id=&quot;mce_marker&quot; data-mce-type=&quot;bookmark&quot;&gt;&amp;#xFEFF;&amp;#x200B;&lt;/span&gt;&lt;/style&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;실습&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/gajicoding/cicd-sample&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/gajicoding/cicd-sample&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1751379469939&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - gajicoding/cicd-sample&quot; data-og-description=&quot;Contribute to gajicoding/cicd-sample development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/gajicoding/cicd-sample&quot; data-og-url=&quot;https://github.com/gajicoding/cicd-sample&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://github.com/gajicoding/cicd-sample&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/gajicoding/cicd-sample&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - gajicoding/cicd-sample&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to gajicoding/cicd-sample development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/강의</category>
      <author>가지코딩</author>
      <guid isPermaLink="true">https://gajicoding.tistory.com/389</guid>
      <comments>https://gajicoding.tistory.com/389#entry389comment</comments>
      <pubDate>Tue, 1 Jul 2025 23:16:40 +0900</pubDate>
    </item>
    <item>
      <title>[  Docker + CI/CD] 1. CICD와 Docker 이해</title>
      <link>https://gajicoding.tistory.com/388</link>
      <description>&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;  목차&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t1&quot;&gt;CI/CD란 무엇인가?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t2&quot;&gt;왜 Docker인가&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t3&quot;&gt;Docker 설치 - Window11&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t4&quot;&gt;Docker 설치 후 Docker Test&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t5&quot;&gt;Docker Image&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;#t6&quot;&gt;Docker Container&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  학습 목표&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CI/CD 사용 이유를 이해합니다.&lt;/li&gt;
&lt;li&gt;Docker 사용 이유를 이해합니다.&lt;/li&gt;
&lt;li&gt;윈도우11 이나 MacOS에서 도커 실행 환경을 구성합니다.&lt;/li&gt;
&lt;li&gt;간단한 Container 서비스 구현 실습을 통해 앱을 실행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t1&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;1. CI/CD란&amp;nbsp;무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; CI/CD &lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Continuous Integration(지속적 통합)과 Continuous Delivery 또는 Continuous Deployment(지속적 제공 또는 지속적 배포)의 약자&lt;/li&gt;
&lt;li&gt;소프트웨어 개발에서 &lt;b&gt;개발 &amp;rarr; 테스트 &amp;rarr; 배포&lt;/b&gt;의 과정을 &lt;b&gt;자동화&lt;/b&gt;하여 더 빠르고 안정적으로 서비스를 제공하기 위한 &lt;b&gt;DevOps의 핵심 개념&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; CI (지속적 통합, Continuous Integration) &lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발자들이 작은 단위로 자주 코드를 통합(merge)하고, 그때마다 자동으로 빌드 및 테스트하는 프로세스&lt;/li&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-end=&quot;356&quot; data-start=&quot;341&quot;&gt;코드 통합 시 충돌 방지&lt;/li&gt;
&lt;li data-end=&quot;382&quot; data-start=&quot;357&quot;&gt;자동화된 테스트를 통해 버그를 초기에 발견&lt;/li&gt;
&lt;li data-end=&quot;399&quot; data-start=&quot;383&quot;&gt;개발자 간 협업 효율 증대&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;399&quot; data-start=&quot;383&quot;&gt;예시
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;435&quot; data-start=&quot;409&quot;&gt;개발자가 GitHub에 코드를 push하면&lt;/li&gt;
&lt;li data-end=&quot;485&quot; data-start=&quot;436&quot;&gt;Jenkins/GitHub Actions 등이 코드를 자동으로 빌드하고 테스트 수행&lt;/li&gt;
&lt;li data-end=&quot;511&quot; data-start=&quot;486&quot;&gt;테스트 통과 여부에 따라 병합 여부 결정&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; CD (지속적 제공/배포, Continuous Delivery / Deployment)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Continuous Delivery (지속적 제공)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CI 이후의 단계로, &lt;b&gt;테스트를 통과한 코드를 자동으로 staging 서버에 배포&lt;/b&gt; 가능한 상태로 만들어 둠.&lt;br /&gt;하지만 &lt;b&gt;실제 프로덕션 배포는 수동&lt;/b&gt;으로 진행.&lt;/li&gt;
&lt;li data-end=&quot;743&quot; data-start=&quot;716&quot;&gt;수동 승인만 하면 바로 운영 서버로 배포 가능&lt;/li&gt;
&lt;li data-end=&quot;760&quot; data-start=&quot;744&quot;&gt;안정성과 배포 준비성 확보&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;760&quot; data-start=&quot;744&quot;&gt;Continuous Deployment (지속적 배포)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;760&quot; data-start=&quot;744&quot;&gt;테스트까지 통과하면 &lt;b&gt;자동으로 운영 서버에 배포까지 수행&lt;/b&gt;하는 방식&lt;/li&gt;
&lt;li data-end=&quot;878&quot; data-start=&quot;849&quot;&gt;사람이 개입하지 않아도 코드 &amp;rarr; 운영까지 자동화됨&lt;/li&gt;
&lt;li data-end=&quot;916&quot; data-start=&quot;879&quot;&gt;빠른 피드백 루프 가능 (ex: A/B 테스트, 빠른 롤백 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; CI/CD 도입 시 이점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;950&quot; data-start=&quot;940&quot;&gt;배포 속도 향상&lt;/li&gt;
&lt;li data-end=&quot;966&quot; data-start=&quot;951&quot;&gt;버그 조기 발견 및 수정&lt;/li&gt;
&lt;li data-end=&quot;977&quot; data-start=&quot;967&quot;&gt;코드 품질 향상&lt;/li&gt;
&lt;li data-end=&quot;1008&quot; data-start=&quot;978&quot;&gt;반복 작업 감소 &amp;rarr; 개발자는 핵심 로직에 집중 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 관련 도구들&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 126px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt; 영역 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt; 도구 예시 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;버전 관리&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Git, GitHub, GitLab&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;CI/CD 파이프라인&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Jenkins, GitHub Actions, GitLab CI/CD, CircleCI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;빌드 도구&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Maven, Gradle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;테스트 자동화&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;JUnit, Mockito, Selenium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;배포 자동화&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Docker, Kubernetes, ArgoCD&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t2&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;2. 왜&amp;nbsp;Docker인가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는 현대 소프트웨어 개발에서 널리 사용되는 컨테이너 기반 가상화 기술이다.&lt;br /&gt;개발, 테스트, 배포 환경을 표준화하고 자동화할 수 있다는 점에서 많은 개발자와 기업이 도입하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Docker 사용 이유&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-pm-slice=&quot;0 0 []&quot;&gt;개발과 배포가 편리하다
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-end=&quot;298&quot; data-start=&quot;242&quot;&gt;Docker 컨테이너는 애플리케이션 실행에 필요한 모든 요소를 포함하므로, 환경 설정이 간단하다.&lt;/li&gt;
&lt;li data-end=&quot;350&quot; data-start=&quot;299&quot;&gt;컨테이너 내부에서 다양한 소프트웨어를 설치하더라도 호스트 운영체제에 영향을 주지 않는다.&lt;/li&gt;
&lt;li data-end=&quot;403&quot; data-start=&quot;351&quot;&gt;어떤 서버에서 실행하더라도 항상 동일한 환경을 보장하기 때문에 배포 오류를 줄일 수 있다.&lt;/li&gt;
&lt;li data-end=&quot;442&quot; data-start=&quot;404&quot;&gt;CI/CD 파이프라인에서 테스트 및 빌드 환경으로 자주 활용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-pm-slice=&quot;0 0 []&quot;&gt;독립성과 확장성이 뛰어나다
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-end=&quot;524&quot; data-start=&quot;471&quot;&gt;애플리케이션을 서로 다른 컨테이너에 배포함으로써 각 애플리케이션이 서로 영향을 주지 않는다.&lt;/li&gt;
&lt;li data-end=&quot;572&quot; data-start=&quot;525&quot;&gt;컨테이너 단위로 수평 확장이 가능하므로, 트래픽 증가에 유연하게 대응할 수 있다.&lt;/li&gt;
&lt;li data-end=&quot;608&quot; data-start=&quot;573&quot;&gt;컨테이너는 가볍고 빠르게 실행되므로, 리소스 효율성도 높다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-pm-slice=&quot;0 0 []&quot;&gt;컨테이너 기술의 사실상 표준이다
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-end=&quot;686&quot; data-start=&quot;640&quot;&gt;Docker는 기존의 가상 머신에 비해 경량화되어 있으며, 실행 속도도 빠르다.&lt;/li&gt;
&lt;li data-end=&quot;729&quot; data-start=&quot;687&quot;&gt;클라우드, DevOps, MSA 환경에서 사실상 표준으로 자리잡고 있다.&lt;/li&gt;
&lt;li data-end=&quot;771&quot; data-start=&quot;730&quot;&gt;다양한 개발 도구 및 플랫폼과의 연동성이 뛰어나며, 생태계도 활발하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t3&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;3. Docker&amp;nbsp;설치&amp;nbsp;-&amp;nbsp;Window11&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;3325&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/odmEt/btsOZkCiuhf/GiushEEmw7aPgM1khBRZx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/odmEt/btsOZkCiuhf/GiushEEmw7aPgM1khBRZx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/odmEt/btsOZkCiuhf/GiushEEmw7aPgM1khBRZx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FodmEt%2FbtsOZkCiuhf%2FGiushEEmw7aPgM1khBRZx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;740&quot; height=&quot;3325&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;3325&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t4&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;4. Docker 설치 후 Docker Test&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; Docker 엔진과 구성 확인 &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751351785496&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker info&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; Container 실행 테스트&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751351821650&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# nginx 이미지 다운받기
docker image pull nginx:1.25.3-alpine

docker images

docker image history nginx:1.25.3-alpine

docker run -d -p 8001:80 --name webserver01 nginx:1.25.3-alpine

docker ps | Select-String &quot;webserver01&quot;

docker port webserver01

curl http://localhost:8001&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t5&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;5. Docker&amp;nbsp;Image&lt;/h2&gt;
&lt;p id=&quot;t5&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Docker&amp;nbsp;Image&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Docker 이미지는 &lt;b&gt;컨테이너 서비스를 위한 실행 환경을 정의한 읽기 전용 템플릿&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;애플리케이션 실행에 필요한 바이너리, 라이브러리, 설정 파일 등을 포함한다.&lt;/li&gt;
&lt;li&gt;특징
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태 저장 없음 (Stateless)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Docker 이미지는 애플리케이션 실행에 필요한 모든 파일과 라이브러리를 자체적으로 포함한다.&lt;/li&gt;
&lt;li&gt;실행 환경에 영향을 받지 않고, &lt;b&gt;어디서든 동일한 애플리케이션을 실행할 수 있다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이미지는 변경되는 상태값을 보유하지 않는다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;불변성 (Immutable)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Docker 이미지는 &lt;b&gt;한 번 생성되면 변경할 수 없다. &lt;/b&gt;&lt;/li&gt;
&lt;li&gt;이미지를 수정하고 싶다면 기존 이미지를 기반으로 &lt;b&gt;새로운 이미지를 생성해야 한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;불변성은 배포 환경의 안정성과 일관성을 높이는 데 기여한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;경량성
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;이미지는 &lt;b&gt;필요한 파일만 포함&lt;/b&gt;하고 있기 때문에, 전통적인 가상 머신에 비해 용량이 작고 전송 속도도 빠르다.&lt;/li&gt;
&lt;li&gt;버전 관리 및 캐싱이 용이하며, 레이어 구조로 인해 중복 없이 효율적으로 관리된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Docker Image 명령어&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt; 목적 &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; 명령어 &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; 설명 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;로컬 이미지 목록 확인&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;docker image ls 또는 docker images&lt;/td&gt;
&lt;td&gt;로컬에 저장된 이미지 목록을 출력한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;이미지 다운로드&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;docker image pull &amp;lt;이미지&amp;gt;&lt;/td&gt;
&lt;td&gt;Docker Hub 또는 다른 레지스트리에서 이미지를 다운로드한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;이미지 업로드&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;docker image push &amp;lt;이미지&amp;gt;&lt;/td&gt;
&lt;td&gt;로컬 이미지를 Docker Hub 등 원격 레지스트리에 업로드한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;이미지 빌드&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;docker image build -t &amp;lt;이름&amp;gt;:&amp;lt;태그&amp;gt; &amp;lt;경로&amp;gt;&lt;/td&gt;
&lt;td&gt;지정된 Dockerfile을 기반으로 이미지를 빌드한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;이미지 삭제&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;docker image rm &amp;lt;이미지&amp;gt;&lt;/td&gt;
&lt;td&gt;지정한 로컬 이미지를 삭제한다. (-f 옵션으로 강제 삭제 가능)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;태그 추가&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;docker image tag &amp;lt;원본&amp;gt; &amp;lt;새이름&amp;gt;:&amp;lt;태그&amp;gt;&lt;/td&gt;
&lt;td&gt;기존 이미지에 새 이름(태그)을 부여한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;이미지 메타 정보 확인&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;docker image inspect &amp;lt;이미지&amp;gt;&lt;/td&gt;
&lt;td&gt;이미지의 상세 정보를 JSON 형식으로 출력한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;이미지 히스토리 확인&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;docker image history &amp;lt;이미지&amp;gt;&lt;/td&gt;
&lt;td&gt;이미지의 레이어별 생성 내역을 확인한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;사용하지 않는 이미지 정리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;docker image prune&lt;/td&gt;
&lt;td&gt;태그가 없는(dangling) 이미지들을 일괄 삭제한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;이미지 내보내기&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;docker image save -o &amp;lt;파일&amp;gt;.tar &amp;lt;이미지&amp;gt;&lt;/td&gt;
&lt;td&gt;이미지를 tar 파일로 저장한다. (백업/전송용)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;이미지 불러오기&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;docker image load -i &amp;lt;파일&amp;gt;.tar&lt;/td&gt;
&lt;td&gt;저장된 tar 파일로부터 이미지를 불러온다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;자주 사용하는 명령어&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751368852424&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 이미지 빌드
docker image build -t myapp:1.0 .

# 2. 빌드된 이미지 확인
docker image ls

# 3. 이미지의 레이어 히스토리 확인
docker image history myapp:1.0

# 4. 이미지 메타 정보 조회
docker image inspect myapp:1.0

# 5. 사용하지 않는 이미지 정리
docker image prune -f&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 id=&quot;t6&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;6. Docker&amp;nbsp;Container&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Docker Container&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Docker Image를 실행한 인스턴스이며, 애플리케이션과 그 실행 환경을 격리된 상태로 실행하는 단위&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 이미지와 컨테이너의 관계 &lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;589&quot; data-start=&quot;547&quot;&gt;이미지(Image): 읽기 전용 템플릿 (설치본, 실행 파일 등)&lt;/li&gt;
&lt;li data-end=&quot;640&quot; data-start=&quot;590&quot;&gt;컨테이너(Container): 이미지에서 실행된 프로세스 (실행 중인 프로그램)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;503&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMxC2d/btsO0vpLfgT/OyKjhtXsDcAiGTOnhyGuF0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMxC2d/btsO0vpLfgT/OyKjhtXsDcAiGTOnhyGuF0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMxC2d/btsO0vpLfgT/OyKjhtXsDcAiGTOnhyGuF0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMxC2d%2FbtsO0vpLfgT%2FOyKjhtXsDcAiGTOnhyGuF0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;407&quot; height=&quot;319&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;503&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 컨테이너 동작 원리&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;805&quot; data-start=&quot;779&quot;&gt;Dockerfile을 통해 이미지 생성&lt;/li&gt;
&lt;li data-end=&quot;831&quot; data-start=&quot;806&quot;&gt;이미지를 기반으로 컨테이너 생성 및 실행&lt;/li&gt;
&lt;li data-end=&quot;871&quot; data-start=&quot;832&quot;&gt;컨테이너는 이미지 위에 &lt;b&gt;쓰기 가능한 레이어&lt;/b&gt;를 추가하여 동작&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;283&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOYqC1/btsO0J2osE5/1NKTSH9cveaROltpQAtPjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOYqC1/btsO0J2osE5/1NKTSH9cveaROltpQAtPjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOYqC1/btsO0J2osE5/1NKTSH9cveaROltpQAtPjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOYqC1%2FbtsO0J2osE5%2F1NKTSH9cveaROltpQAtPjK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;511&quot; height=&quot;210&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;283&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div&gt;
&lt;style&gt;  #article-view img {  	border: 2px solid #000;  }  #tinymce img {  	border: 2px solid #000;  }&lt;\/style&gt;&lt;\/div&gt;&quot;&lt;span id=&quot;mce_marker&quot; data-mce-type=&quot;bookmark&quot;&gt;&amp;#xFEFF;&amp;#x200B;&lt;/span&gt;&lt;/style&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Docker Container 명령어&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 458px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt; 목적 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt; 명령어 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt; 설명 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;컨테이너 실행&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;docker container run&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;새로운 컨테이너를 생성하고 실행한다. (docker run 과 동일)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;&lt;b&gt;실행 중인 컨테이너 목록 확인&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;docker container ls&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;현재 실행 중인 컨테이너 목록을 출력한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;모든 컨테이너 목록 확인&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;docker container ls -a&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;중지된 컨테이너를 포함한 전체 목록을 출력한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;&lt;b&gt;컨테이너 내부 명령 실행&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;docker container exec -it &amp;lt;컨테이너&amp;gt; &amp;lt;명령&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;실행 중인 컨테이너 내부에서 명령을 실행한다 (예: bash 진입).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;&lt;b&gt;컨테이너 상태 상세 조회&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;docker container inspect &amp;lt;컨테이너&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;컨테이너의 설정, 네트워크, 볼륨 등 상세 정보를 JSON 형식으로 출력한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;로그 확인&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;docker container logs &amp;lt;컨테이너&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;해당 컨테이너의 stdout/stderr 로그를 출력한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;프로세스 확인&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;docker container top &amp;lt;컨테이너&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;컨테이너 내부에서 실행 중인 프로세스 목록을 출력한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;&lt;b&gt;리소스 사용량 확인&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;docker container stats&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;컨테이너의 CPU, 메모리, 네트워크 사용량 등을 실시간으로 확인한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;컨테이너 일시 중단&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;docker container pause &amp;lt;컨테이너&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;컨테이너의 모든 프로세스를 일시 중단한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 38px;&quot;&gt;
&lt;td style=&quot;height: 38px;&quot;&gt;&lt;b&gt;일시 중단 해제&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 38px;&quot;&gt;docker container unpause &amp;lt;컨테이너&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 38px;&quot;&gt;pause 상태의 컨테이너를 다시 실행 상태로 전환한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;컨테이너 중지&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;docker container stop &amp;lt;컨테이너&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;컨테이너를 정상 종료(SIGTERM &amp;rarr; SIGKILL)한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;컨테이너 강제 종료&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;docker container kill &amp;lt;컨테이너&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;즉시 SIGKILL 신호로 컨테이너를 종료한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;컨테이너 재시작&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;docker container restart &amp;lt;컨테이너&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;컨테이너를 중지한 뒤 다시 시작한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;컨테이너 삭제&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;docker container rm &amp;lt;컨테이너&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;중지된 컨테이너를 삭제한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;&lt;b&gt;사용하지 않는 컨테이너 일괄 정리&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;docker container prune&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;중지된 모든 컨테이너를 한 번에 삭제한다. (확인 메시지 있음)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt; &lt;b&gt;자주 사용하는 명령어&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751369188935&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. nginx 컨테이너 실행
docker run --name web01 -d -p 8080:80 nginx

# 2. 실행 중인 컨테이너 목록 확인
docker container ls

# 3. 컨테이너 내부에 접속
docker exec -it web01 bash

# 4. 컨테이너 중지 및 삭제
docker stop web01
docker rm web01&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Backend/강의</category>
      <author>가지코딩</author>
      <guid isPermaLink="true">https://gajicoding.tistory.com/388</guid>
      <comments>https://gajicoding.tistory.com/388#entry388comment</comments>
      <pubDate>Tue, 1 Jul 2025 20:32:19 +0900</pubDate>
    </item>
  </channel>
</rss>