<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>kevin_kim.log</title>
        <link>https://velog.io/</link>
        <description>머리로 이해한것을 적으며 기록하자!</description>
        <lastBuildDate>Mon, 11 Mar 2024 04:56:07 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>kevin_kim.log</title>
            <url>https://velog.velcdn.com/images/kevin_kim/profile/056bc999-f332-4e69-93c2-e4cd3c2bb79a/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. kevin_kim.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/kevin_kim" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[DBMS 종류와 특징 및 장단점 정리]]></title>
            <link>https://velog.io/@kevin_kim/DBMS-%EC%A2%85%EB%A5%98%EC%99%80-%ED%8A%B9%EC%A7%95-%EB%B0%8F-%EC%9E%A5%EB%8B%A8%EC%A0%90-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@kevin_kim/DBMS-%EC%A2%85%EB%A5%98%EC%99%80-%ED%8A%B9%EC%A7%95-%EB%B0%8F-%EC%9E%A5%EB%8B%A8%EC%A0%90-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 11 Mar 2024 04:56:07 GMT</pubDate>
            <description><![CDATA[<h3 id="oracle">Oracle</h3>
<ul>
<li><p>특징</p>
<ul>
<li>대용량의 데이터 관리를 지원</li>
<li>물리적으로 상이한 다른 컴퓨터에 있는 데이터베이스를 논리적 데이터베이스로 결합이 가능</li>
<li>다른 OS로 이동해도 작동이 가능해 이식성이 높음</li>
</ul>
</li>
<li><p>장점</p>
<ul>
<li><p>다른 데이터베이스 보다 고성능의 트랜잭션을 처리</p>
<blockquote>
<p>고성능을 낼 수 있는 이유</p>
<ul>
<li>옵티마이저를 활용하여 쿼리 실행 계획을 최적화<ul>
<li>옵티마이저란 쿼리를 분석하고 실행 계획을 생성하는 엔진</li>
</ul>
</li>
<li>쿼리를 분석하고 쿼리를 실행할 때 발생하는 비용(CPU, I/O)에 따른 계획을 생성하여 가장 비용이 적은 계획을 선택</li>
<li>인덱스를 사용하여 데이터의 검색 속도 향상</li>
<li>자주 엑세스 되는 데이터를 메모리에 캐싱하여 디스크 엑세스를 줄이고 버퍼 캐시를 통해 변경된 데이터를 일시적으로 메모리에 보관하여 디스크에 대한 입출력을 줄임</li>
</ul>
</blockquote>
</li>
<li><p>Multiple Database 튜닝이 가능 하며 다수의 사용자가 동시에 접근 가능</p>
</li>
<li><p>높은 가용성을 보장</p>
<blockquote>
<p>높은 가용성을 보장하는 이유</p>
<ul>
<li>실시간 백업 및 복구 기능을 제공하여 데이터 손실을 최소화 하고 신속하게 복원할 수 있음</li>
<li>Oracle Data Guard는 데이터베이스의 높은 가용성을 제공하는 솔루션 중 하나입니다. 이는 주 데이터베이스와 이를 보조하는 스탠바이 데이터베이스 간에 데이터를 동기화하여, 주 데이터베이스에 장애가 발생한 경우 스탠바이 데이터베이스로 즉시 전환하여 서비스 중단을 최소화</li>
<li>Oracle RAC은 여러 노드 간에 데이터베이스를 공유하여 고가용성과 확장성을 제공합니다. 이를 통해 시스템 장애가 발생해도 사용자는 다른 노드에서 서비스를 계속 받을 수 있음</li>
</ul>
</blockquote>
</li>
</ul>
</li>
<li><p>단점</p>
<ul>
<li>기능이 많고 안정성이 높은 대신 상당히 비싼 비용이 들어감</li>
<li>많은 기능들이 있지만 이로 인해 관리 및 구성이 복잡할 수 있음</li>
<li>DBMS를 운영하기 위해 많은 하드웨어의 지원이 필요함</li>
</ul>
</li>
</ul>
<h3 id="mysql">MySQL</h3>
<ul>
<li><p>특징</p>
<ul>
<li><p>오픈소스 DBMS로 상용 목적으로 사용하지 않으면 비용이 발생하지 않음</p>
</li>
<li><p>MySQL 서버는 다른 DBMS에 비해 작고 빠르며 설치가 간단함</p>
</li>
<li><p>읽기 전용 쿼리를 처리하는데 빠른 속도를 보임</p>
</li>
<li><p>여러 스토리지 엔진을 지원하기 때문에 사용자의 요구에 맞게 엔진을 선택할 수 있음</p>
<blockquote>
<p>스토리지 엔진 종류</p>
<ul>
<li>InnoDB<ul>
<li>가장 많이 사용되는 엔진으로 ACID 트랜잭션을 지원하며 뛰어난 성능과 장애 복구 기능을 가진 대표적인 엔진</li>
<li>결재 정보와 같이 무결성을 가져야 하고 손실이 되면 안되는 중요한 데이터를 필요로 할 때 사용</li>
<li>자동 데드락 감지, 외래키 지원, 자동화 장애 복구등을 지원</li>
</ul>
</li>
<li>MyISAM<ul>
<li>full-text인덱싱, 압축, 공간 (Geographic Information System, GIS, 지리정보시스템) 함수 등을 지원하지만 트랜잭션을 지원하지 않기 때문에 에 InnoDB보다 심플하고 기본적으로 빠르지만 동시성 제어가 어려움</li>
<li>Read 쿼리가 많은 DW 환경에서 많이 사용된다.</li>
</ul>
</li>
<li>Memory<ul>
<li>메모리에 데이터를 저장하는 엔진으로 트랜잭션을 지원하지 않고 테이블 레벨 락을 사용</li>
<li>메모리를 사용하기 때문에 기본적으로 속도가 아주 빠르지만 데이터를 잃어버릴 위험이 있기 때문에 빠른 처리가 필요한 임시 테이블로 많이 사용
ex) 주기적으로 집계되는 데이터의 결과 캐싱용</li>
</ul>
</li>
<li>Archive<ul>
<li>아주 빠르게 INSERT 쿼리를 처리할 수 있는 엔진으로 데이터 쓰기를 버퍼링하고 각 행이 삽입될 때마다 zlib으로 압축</li>
<li>Index를 지원하지 않으며 INSERT/REPLACE/SELECT 쿼리와 같이 데이터를 삽입하거나 읽는 것은 가능하지만 DELETE/UPDATE와 같이 데이터를 삭제하거나 수정할 수 없음</li>
<li>transaction을 지원하지 않고 row-level locking을 사용하며 주로 많은 양의 로그성 데이터를 저장하고 읽는데 주로 사용</li>
</ul>
</li>
</ul>
</blockquote>
</li>
</ul>
</li>
<li><p>장점</p>
<ul>
<li>오픈소스이기 때문에 상용 목적이 아니면 비용이 발생하지 않음</li>
<li>구조가 간단하고 가볍기 때문에 사용이 쉬움</li>
<li>다양한 운영체제에서 사용할 수 있음</li>
<li>보안 수준이 높음</li>
</ul>
</li>
<li><p>단점</p>
<ul>
<li>복잡한 쿼리에서 성능이 저하됨</li>
<li>대용량 데이터 조회 시 성능 저하가 발생</li>
</ul>
</li>
</ul>
<h3 id="postgresql">Postgresql</h3>
<ul>
<li><p>특징</p>
<ul>
<li>오픈소스로 라이센스 비용이 들지 않음</li>
<li>관계형과 객체 지향 기능을 결합한 <strong>ORDBMS(Object - Oriented Database Management System)</strong>으로 구조화된 데이터 유형과 복잡한 데이터 유형을 모두 관리</li>
<li>사용자 정의 함수 및 저장 프로시저를 지원하며 C, C++, Java 등과 같은 다른 프로그래밍 언어로 만든 사용자 정의 함수를 통합할 수 있음</li>
</ul>
</li>
<li><p>장점</p>
<ul>
<li><p>복잡한 쿼리와 대규모 서비스에 특화됨</p>
</li>
<li><p>ANSI SQL 표준을 준수</p>
<ul>
<li>전체 179 항목 중 170항목인 약 95%의 표준 SQL 지원</li>
</ul>
</li>
<li><p>다양한 데이터 유형을 지원</p>
<ul>
<li>배열, jsonb, json 형식으로 저장 가능</li>
</ul>
</li>
<li><p>다중 버전 동시성 제어(Multi-Version Concurrency Control) 지원</p>
<blockquote>
<p>다중 버전 동시성 제어란?</p>
<ul>
<li>갱신 / 변경된 데이터를 이전 데이터와 버전을 달리해 버전을 관리하고 이를 기반으로 일관성을 유지</li>
<li>버전 데이터가 계속 쌓인다는 단점이 있지만 일반적인 RDBMS보다 빠르게 작동한다는 장점이 있음</li>
<li>버전 데이터는</li>
</ul>
</blockquote>
</li>
<li><p>관계형과 객체 지향 기능을 결합한 ORDBMS</p>
<ul>
<li>객체 지향 프로그래밍과 유사한 방식으로 데이터를 모델링</li>
<li>데이터베이스 내의 요소들(테이블, 행 등)을 객체로 취급하고 이들 사이의 관계를 객체 간의 관계로 표현</li>
<li>테이블간의 상속을 지원하여 하나의 테이블이 다른 테이블의 속성과 기능을 상속 받을 수 있음</li>
</ul>
</li>
</ul>
</li>
<li><p>단점</p>
<ul>
<li>Update 성능이 떨어짐<ul>
<li>update를 진행하게 되면 delete한 후 insert하는 방식으로 진행됨</li>
<li>그렇기 때문에 update가 진행되면 인덱스 정보도 다시 업데이트가 되어야 하기 때문에 성능이 떨어짐</li>
</ul>
</li>
<li>메모리 성능이 떨어짐<ul>
<li>새로운 클라이언트를 연결 할 때 마다 새로운 프로세스를 fork 하기 때문에 메모리 성능이 떨어짐</li>
</ul>
</li>
</ul>
</li>
<li><p>사용이유</p>
<ul>
<li>데이터 무결성: 데이터 무결성을 엄격하게 유지하여 신뢰성 향상</li>
<li>데이터 복잡성 처리 : 복잡한 데이터 모델과 쿼리 처리에 적합</li>
<li>비용 절감 : 오픈 소스로 비용이 없음</li>
<li>확장성과 고가용성 : 대규모 어플리케이션과 클러스터 지원</li>
<li>고급기능 : 고급기능, 사용자 정의 함수, 프로시저, 트리거, 전문검색 사용 가능</li>
</ul>
</li>
</ul>
<h3 id="ms-sql">MS SQL</h3>
<ul>
<li>특징<ul>
<li>Microsoft에서 개발한 관계형 데이터베이스 관리 시스템으로 주로 Window 환경에 최적화되어 있음</li>
<li>오라클 보다 가격이 저렴함</li>
</ul>
</li>
<li>장점<ul>
<li>.NET Framework와 연동하여 데이터 베이스와의 상호작용이 용이</li>
<li>Microsoft에서 개발하고 유지보수 하기 때문에 풍부한 지원 및 커뮤니티를 제공</li>
</ul>
</li>
<li>단점<ul>
<li>Window환경에서만 동작<ul>
<li>Linux도 지원되는 부분이 있지만 거의 Window에서만 사용할 수 있음</li>
</ul>
</li>
<li>라이센스 비용이 들어감</li>
</ul>
</li>
</ul>
<h3 id="참고">참고</h3>
<p><a href="https://nomadlee.com/mysql-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80-%EC%97%94%EC%A7%84-%EC%A2%85%EB%A5%98-%EB%B0%8F-%ED%8A%B9%EC%A7%95/">https://nomadlee.com/mysql-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80-%EC%97%94%EC%A7%84-%EC%A2%85%EB%A5%98-%EB%B0%8F-%ED%8A%B9%EC%A7%95/</a>
<a href="https://velog.io/@onejaejae/DB-MVCC">https://velog.io/@onejaejae/DB-MVCC</a>
<a href="https://daniel6364.tistory.com/entry/PostgreSQL-RDBMS">https://daniel6364.tistory.com/entry/PostgreSQL-RDBMS</a>
<a href="https://www.integrate.io/ko/blog/postgresql-vs-mysql-which-one-is-better-for-your-use-case-ko/">https://www.integrate.io/ko/blog/postgresql-vs-mysql-which-one-is-better-for-your-use-case-ko/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로젝트 7일차 회고]]></title>
            <link>https://velog.io/@kevin_kim/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-7%EC%9D%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@kevin_kim/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-7%EC%9D%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 28 Aug 2023 17:31:35 GMT</pubDate>
            <description><![CDATA[<h2 id="작업내용">작업내용</h2>
<hr>
<h3 id="1-상품-수정-및-조회-페이지-구현">1. 상품 수정 및 조회 페이지 구현</h3>
<ul>
<li>수정 페이지에서 중요했던 내용<ol>
<li>수정 시 옵션이 추가, 변경, 삭제 되었을 때 어떻게 값을 넘겨줄 것인지</li>
</ol>
</li>
</ul>
<h3 id="2-상품-수정-및-조회-구현">2. 상품 수정 및 조회 구현</h3>
<ul>
<li><p>상품 수정 시 중요했던 내용</p>
<ol>
<li><p>상품 수정 시 상품 옵션이 삭제 되거나 추가되는 경우의 경우의 수를 생각해서 작업을 해야 했음</p>
<ul>
<li><p>3가지 경우의 수로 나누어서 작업 진행</p>
<pre><code class="language-java">// 5. 상품 옵션 수정
// 5-1. 기존 옵션이 더 많을 때(상품 옵션이 삭제 되었을 때)
if(findProduct.getProductOptionList().size() &gt; product.getProductOptionList().size()) {
  for (int i = 0; i &lt; findProduct.getProductOptionList().size(); i++) {
      ProductOption productOption = findProduct.getProductOptionList().get(i);

      if(i &lt; product.getProductOptionList().size()) {
          ProductOptionDto productOptionDto = product.getProductOptionList().get(i);

          if(productOption.getId().equals(productOptionDto.getId())) {
              productOption.changeProductOption(productOptionDto, findProduct);
          }
      } else {
          productOptionRepository.delete(productOption);
      }
  }
}
// 5-2. 수정하는 옵션이 더 많을 때
else if (findProduct.getProductOptionList().size() &lt; product.getProductOptionList().size()) {
  for (int i = 0; i &lt; product.getProductOptionList().size(); i++) {
      ProductOptionDto productOptionDto = product.getProductOptionList().get(i);

      if(i &lt; findProduct.getProductOptionList().size()) {
          ProductOption productOption = findProduct.getProductOptionList().get(i);
          if(productOption.getId().equals(productOptionDto.getId())) {
              productOption.changeProductOption(productOptionDto, findProduct);
          } else {
              // 기존 옵션을 삭제하고 새로운 옵션 추가
              productOptionRepository.delete(productOption);
              addProductOption(findProduct, productOptionDto);
          }
      } else {
          // 옵션 추가
          addProductOption(findProduct, productOptionDto);
      }
  }
}
// 5-3. 리스트의 갯수가 같을 때
else {
  for (int i = 0; i &lt; product.getProductOptionList().size(); i++) {
      ProductOptionDto productOptionDto = product.getProductOptionList().get(i);
      ProductOption productOption = findProduct.getProductOptionList().get(i);

      if(productOption.getId().equals(productOptionDto.getId())) {
          productOption.changeProductOption(productOptionDto, findProduct);
      } else {
          // 기존 옵션을 삭제하고 새로운 옵션 추가
          productOptionRepository.delete(productOption);
          addProductOption(findProduct, productOptionDto);
      }
  }
}

// 상품 옵션 추가
private void addProductOption(Product findProduct, ProductOptionDto productOptionDto) {
  productOptionRepository.save(ProductOption.builder()
          .size(productOptionDto.getSize())
          .color(productOptionDto.getColor())
          .stock(productOptionDto.getStock())
          .product(findProduct)
          .build());
}</code></pre>
</li>
</ul>
</li>
<li><p>상품 수정 시 이미지가 변경이 되었으면 S3에서 기존 이미지를 삭제 후 새로 업로드 해줘야함</p>
<pre><code class="language-java">
 //파일 삭제 코드를 구현하여 삭제함
 if(mainImg != null) {
     // 인덱스값 추출
     AttachDetail findAttachDetail = findProduct.getAttach().getAttachDetailList().get(0);
     String filename = findAttachDetail.getFileUrl();
     int idx = filename.indexOf(&quot;/product/&quot;);
     // 삭제할 파일명 추출
     String deleteFilename = findAttachDetail.getFileUrl().substring(idx + 1);
     // 이미지 삭제
     s3UploadService.deleteFile(deleteFilename);
     // 이미지 업로드
     String mainImgFileUrl = s3UploadService.upload(mainImg, &quot;product&quot;);
     // 이미지 수정
     findAttachDetail.changeFile(
             findAttachDetail.getId(),
             mainImgFileUrl,
             mainImg.getSize(),
             mainImg.getOriginalFilename(),
             findProduct.getAttach());

     log.info(&quot;대표 이미지 수정 완료&quot;);
 }

 // s3UploadService.deleteFile(fileName)
 public void deleteFile(String fileName){
     log.info(&quot;파일삭제 시작&quot;);
     DeleteObjectRequest request = new DeleteObjectRequest(bucket, fileName);
     amazonS3Client.deleteObject(request);
     log.info(&quot;파일삭제 끝&quot;);
 }</code></pre>
</li>
</ol>
</li>
</ul>
<h3 id="3-오류-수정">3. 오류 수정</h3>
<ul>
<li><p>상품 등록, 수정 페이지에서 옵션을 추가 하면 맨 마지막에 추가가 되는 것이 아니라 두번째 칸에 계속 추가되는 버그를 수정</p>
<pre><code class="language-jsx">  $(document).on(&quot;click&quot;, &quot;#add-button&quot;, function() {
        let newItem = $(&quot;#item-&quot; + nowItemCnt).clone(); // 새로운 아이템 복제
        newItem.attr(&quot;id&quot;, &quot;item-&quot; + ++nowItemCnt); // 아이템 ID 설정
        newItem.find(&quot;input[type=&#39;text&#39;]&quot;).val(&quot;&quot;); // 입력 필드 초기화
        newItem.find(&quot;select&quot;).prop(&quot;selectedIndex&quot;, 0); // 셀렉트 박스 초기화
              // 이 부분을 밑에 코드로 수정
              // $(&quot;#item&quot;).after(newItem); 
        $(&quot;#item-&quot; + (nowItemCnt - 1)).after(newItem); // 복제한 아이템 추가
    });</code></pre>
</li>
</ul>
<h3 id="🚀-trouble-shooting">🚀 Trouble Shooting</h3>
<ol>
<li><p>DB에 한글이 깨져서 들어감</p>
<ul>
<li><p>문제 : DB에 한글이 깨져서 들어감</p>
</li>
<li><p>원인 : DB를 확인해보니 character set 이 utf-8로 되어 있지 않음</p>
</li>
<li><p>해결 : docker를 이용하여 mysql을 올려놓은 상태라 docker 안에 있는 mysql에 접속하여 utf-8로 변경</p>
<pre><code class="language-bash">  # 해당 컨테이너의 bash로 들어감
  docker exec -it &lt;container_id_or_name&gt; /bin/bash

  # 설정파일을 생성
  cat &lt;&lt; &#39;EOF&#39; &gt; /etc/mysql/conf.d/utf8.cnf

  # 컨테이너 리스타트 
  docker restart &lt;container_id_or_name&gt;</code></pre>
<pre><code class="language-bash">  # 해당 내용 적용

  [client]
  default-character-set = utf8

  [mysqld]
  init_connect = SET collation_connection = utf8_general_ci
  init_connect = SET NAMES utf8
  character-set-server = utf8
  collation-server = utf8_general_ci

  [mysqldump]
  default-character-set = utf8

  [mysql]
  default-character-set = utf8
  EOF</code></pre>
</li>
</ul>
</li>
<li><p>S3에 파일 삭제를 요청하면 파일이 삭제 안됨</p>
<ul>
<li><p>문제 : S3에 파일 삭제를 요청하면 파일이 삭제 안됨</p>
</li>
<li><p>원인 : 파일을 product 폴더에 관리중인데 파일명을 폴더명/파일명 이런식으로 전달해야 하는데 폴더명 앞에 /가 하나 더 붙어 있었음..</p>
</li>
<li><p>해결 : /를 제거하여 해결</p>
<pre><code class="language-java">if(mainImg != null) {
  // 인덱스값 추출
  AttachDetail findAttachDetail = findProduct.getAttach().getAttachDetailList().get(0);
  String filename = findAttachDetail.getFileUrl();
  //int idx = filename.indexOf(&quot;/product/&quot;);
      int idx = filename.indexOf(&quot;product/&quot;);
  // 삭제할 파일명 추출
  String deleteFilename = findAttachDetail.getFileUrl().substring(idx + 1);
  // 이미지 삭제
  s3UploadService.deleteFile(deleteFilename);
  // 이미지 업로드
  String mainImgFileUrl = s3UploadService.upload(mainImg, &quot;product&quot;);
  // 이미지 수정
  findAttachDetail.changeFile(
          findAttachDetail.getId(),
          mainImgFileUrl,
          mainImg.getSize(),
          mainImg.getOriginalFilename(),
          findProduct.getAttach());

  log.info(&quot;대표 이미지 수정 완료&quot;);
}</code></pre>
</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로젝트 6일차 회고 ]]></title>
            <link>https://velog.io/@kevin_kim/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-6%EC%9D%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@kevin_kim/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-6%EC%9D%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 28 Aug 2023 17:29:45 GMT</pubDate>
            <description><![CDATA[<h2 id="작업내용">작업내용</h2>
<hr>
<h3 id="1-관리자-상품-등록-구현">1. 관리자 상품 등록 구현</h3>
<ul>
<li>S3로 이미지 업로드</li>
</ul>
<h3 id="s3-iam-권한-설정-및-s3-연결">S3 IAM 권한 설정 및 S3 연결</h3>
<ol>
<li><p>IAM 생성 </p>
<p> <img src="https://velog.velcdn.com/images/kevin_kim/post/0aab1262-a344-4e54-b32e-eab4801c5120/image.png" alt=""></p>
</li>
</ol>
<ul>
<li>S3에만 접근을 할 사용자 이기 때문에 S3FullAccess 권한을 넣어줌</li>
</ul>
<ol start="2">
<li><p>보안 자격증명 (엑세스 키 발급)</p>
<p> <img src="https://velog.velcdn.com/images/kevin_kim/post/0ac74dd5-2055-47b2-9c78-c6475f03d4b7/image.png" alt=""></p>
</li>
</ol>
<ol start="3">
<li><p>application.yml 작성</p>
<pre><code class="language-yaml"> cloud:
   aws:
     credentials:
       access-key: {다운받은 키 파일 중 Access Key ID}
       secret-key: {다운받은 키 파일 중 Secret Access Key}
     region:
       static: ap-northeast-2
     stack:
       auto: false</code></pre>
</li>
<li><p>S3 bucket 생성</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/kevin_kim/post/77a59c83-9017-421b-9940-ec95ff9523e4/image.png" alt=""></p>
<p>   <img src="https://velog.velcdn.com/images/kevin_kim/post/3e49aad2-ed34-4ba3-9cf4-071c87409608/image.png" alt=""></p>
<ul>
<li>외부에서 접근이 가능하도록 퍼블릭으로 설정</li>
</ul>
<ol start="5">
<li><p>S3 버킷 정책 설정</p>
<pre><code class="language-yaml"> # 버킷 설정
 {
   &quot;Version&quot;: &quot;2012-10-17&quot;,
   &quot;Statement&quot;: [
     {
       &quot;Sid&quot;: &quot;AddPerm&quot;,
       &quot;Effect&quot;: &quot;Allow&quot;,
       &quot;Principal&quot;: &quot;*&quot;,
       &quot;Action&quot;: &quot;s3:*&quot;,
       &quot;Resource&quot;: &quot;arn:aws:s3:::버킷 이름/*&quot;
     }
   ]
 }

 # CORS 설정
 [
   {
     &quot;AllowedOrigins&quot;: [&quot;*&quot;],
     &quot;AllowedMethods&quot;: [&quot;GET&quot;, &quot;PUT&quot;, &quot;POST&quot;, &quot;HEAD&quot;],
     &quot;AllowedHeaders&quot;: [&quot;*&quot;],
     &quot;ExposeHeaders&quot;: [&quot;x-amz-server-side-encryption&quot;, &quot;x-amz-request-id&quot;, &quot;x-amz-id-2&quot;],
     &quot;MaxAgeSeconds&quot;: 3000
   }
 ]</code></pre>
</li>
<li><p>S3Config 설정</p>
<pre><code class="language-java"> package com.likelion.catdogpia.config;

 import com.amazonaws.auth.AWSCredentials;
 import com.amazonaws.auth.AWSStaticCredentialsProvider;
 import com.amazonaws.auth.BasicAWSCredentials;
 import com.amazonaws.services.s3.AmazonS3;
 import com.amazonaws.services.s3.AmazonS3ClientBuilder;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;

 // application.yml(application-secret) 파일에서 설정하였던 값들을 가져와, 이를 통하여 AmazonS3Client를 Bean 등록
 @Configuration
 public class S3Config {
     @Value(&quot;${cloud.aws.credentials.access-key}&quot;)
     private String accessKey;

     @Value(&quot;${cloud.aws.credentials.secret-key}&quot;)
     private String secretKey;

     @Value(&quot;${cloud.aws.region.static}&quot;)
     private String region;

     @Bean
     public AmazonS3 amazonS3Client() {
         AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);

         return AmazonS3ClientBuilder
                 .standard()
                 .withCredentials(new AWSStaticCredentialsProvider(credentials))
                 .withRegion(region)
                 .build();
     }
 }</code></pre>
<ol>
<li><strong><code>AmazonS3Client</code></strong>: Amazon S3 서비스와 상호 작용하는 클라이언트 객체로 S3 서비스에 파일을 업로드, 다운로드, 삭제 등의 작업을 수행하는 데 사용</li>
<li><strong><code>BasicAWSCredentials</code></strong>: AWS 계정의 액세스 키와 시크릿 키를 사용하여 AWS 서비스에 액세스하는 인증 정보</li>
<li><strong><code>AmazonS3ClientBuilder</code></strong>: AmazonS3 클라이언트를 생성하기 위한 빌더 클래스 AWS 설정 및 인증 정보, 지역(region) 등을 설정하여 클라이언트를 생성</li>
<li><strong><code>withCredentials</code></strong>: 생성된 클라이언트에 인증 정보를 제공</li>
<li><strong><code>withRegion</code></strong>: Amazon S3의 리전(region)을 설정</li>
</ol>
</li>
</ol>
<h3 id="🚀-trouble-shooting">🚀 Trouble Shooting</h3>
<ol>
<li><p>ajax로 파일과 다른 데이터들이 함께 controller로 넘어가지 않음</p>
<ul>
<li><p>문제 : @RequestBody로 데이터를 받으면 415 에러 발생</p>
</li>
<li><p>원인 : FormData()를 사용하면 @RequestBody로 받을 수 없음</p>
<pre><code class="language-jsx">  &lt;script&gt;
    //== 등록 요청 ==//
      $(&quot;#submitBnt&quot;).on(&quot;click&quot;, function() {
        const productData = {
          categoryId: $(&quot;#selectedSubCategory :selected&quot;).val(),
          name: $(&quot;#name&quot;).val(),
          status: $(&quot;#status&quot;).val(),
          price: parseInt($(&quot;#price&quot;).val()),
          productOptionList: []
        };

        const formData = new FormData();

        const mainImg = $(&quot;#mainImg&quot;)[0].files[0];
        formData.append(&quot;mainImg&quot;, mainImg);

        const detailImg = $(&quot;#detailImg&quot;)[0].files[0];
        formData.append(&quot;detailImg&quot;, detailImg);

        $(&quot;#items-container .item&quot;).each(function() {
          const color = $(this).find(&quot;input.color&quot;).val();
          const size = $(this).find(&quot;select.size&quot;).val();
          const stock = $(this).find(&quot;input.stock&quot;).val();

          if (color &amp;&amp; size &amp;&amp; stock) {
            const optionData = {
              color: color,
              size: size,
              stock: parseInt(stock)
            };
            productData.productOptionList.push(optionData);
          }
        });
        formData.append(&quot;productDto&quot;, JSON.stringify(productData));

        $.ajax({
          type: &quot;POST&quot;,
          url: &quot;/admin/products/create&quot;,
          contentType: false,
          processData: false,
          enctype: &#39;multipart/form-data&#39;,
          data: formData,
          success: function(response) {
            console.log(&quot;Product created successfully!&quot;);
            window.location.href = &quot;/admin/products&quot;;
          },
          error: function(error) {
            console.error(&quot;Error creating product:&quot;, error);
          }
        });
      });
  &lt;/script&gt;

  // Controller
  public String productCreate(
              @RequestParam(&quot;mainImg&quot;) MultipartFile mainImg,
              @RequestParam(&quot;detailImg&quot;) MultipartFile detailImg,
              @RequestBody ProductDto productDto
      ) {</code></pre>
</li>
<li><p>해결 :  RequestParam으로 받아서 json으로 넘어온걸 Controller에서 객체형식으로 변경하기로 함</p>
<pre><code class="language-java">  // Controller 수정
  public String productCreate(
              @RequestParam(&quot;mainImg&quot;) MultipartFile mainImg,
              @RequestParam(&quot;detailImg&quot;) MultipartFile detailImg,
              @RequestParam(&quot;productDto&quot;) String productDto
    ) throws JsonProcessingException {

                  ObjectMapper objectMapper = new ObjectMapper();
          ProductDto product = objectMapper.readValue(productDto, ProductDto.class);
          log.info(&quot;product toString : &quot; + product.toString());
      }
</code></pre>
</li>
</ul>
</li>
<li><p>S3 업로드 시 오류 발생</p>
<ul>
<li>문제 : S3 업로드 The bucket does not allow ACLs (Service: Amazon S3; Status Code: 400; Error Code: AccessControlListNotSupported;) 오류 발생</li>
<li>원인 : S3 버킷에서 액세스 제어 목록(Access Control List, ACL)을 허용하지 않아서 발생</li>
<li>결과 : ACL을 사용할 수 없는 경우에는 해당 기능을 사용하지 않도록 버킷 설정을 변경</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로젝트 5일차 회고]]></title>
            <link>https://velog.io/@kevin_kim/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-5%EC%9D%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@kevin_kim/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-5%EC%9D%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 28 Aug 2023 17:25:56 GMT</pubDate>
            <description><![CDATA[<h3 id="1-관리자-상품목록-관리-화면-구현-및-기능-구현">1. 관리자 상품목록 관리 화면 구현 및 기능 구현</h3>
<ul>
<li>JPA를 활용하여 페이지네이션 처리</li>
<li>JPA를 활용한 페이지 네이션 추가</li>
</ul>
<h3 id="🚀-trouble-shooting">🚀 Trouble Shooting</h3>
<ol>
<li><p>페이지네이션 처리가 안됨</p>
<ul>
<li><p>문제 : 페이지네이션 처리가 안됨</p>
</li>
<li><p>원인 : 페이지네이션 처리할 때 js function을 사용하는데 해당 함수가 is not defined 가 발생</p>
<pre><code class="language-jsx">  &lt;script&gt;
    // 클릭 이벤트
    $(document).ready(function() {
      // 페이지 이동
      function navigateToPage(page) {
        let filter = $(&#39;#filter&#39;).val();
        let keyword = $(&#39;#keyword&#39;).val();
        let hidden = $(&#39;#hidden&#39;).val();

        if(hidden != null) {
          filter = encodeURIComponent(filter);
          hidden = encodeURIComponent(hidden);
          location.href = &#39;/admin/products?page=&#39; + page + &#39;&amp;filter=&#39; + filter + &#39;&amp;keyword=&#39; + hidden;
        } else if(keyword != null){
          filter = encodeURIComponent(filter);
          keyword = encodeURIComponent(keyword);
          location.href = &#39;/admin/products?page=&#39; + page + &#39;&amp;filter=&#39; + filter + &#39;&amp;keyword=&#39; + keyword;
        } else {
          location.href = &#39;/admin/products?page=&#39; + page;
        }
      }
      )};
  &lt;/script&gt;</code></pre>
</li>
<li><p>해결 : ready 밖으로 이동하였더니 정상동작</p>
<ul>
<li><p>JS를 로딩하는 도중 문제가 발생하는 것으로 생각하여 변경하였더니 정상적으로 동작!</p>
<pre><code class="language-jsx">&lt;script&gt;
// 클릭 이벤트
$(document).ready(function() {
  // 다른 함수 사용중
  )};

      // 페이지 이동
  function navigateToPage(page) {
    let filter = $(&#39;#filter&#39;).val();
    let keyword = $(&#39;#keyword&#39;).val();
    let hidden = $(&#39;#hidden&#39;).val();

    if(hidden != null) {
      filter = encodeURIComponent(filter);
      hidden = encodeURIComponent(hidden);
      location.href = &#39;/admin/products?page=&#39; + page + &#39;&amp;filter=&#39; + filter + &#39;&amp;keyword=&#39; + hidden;
    } else if(keyword != null){
      filter = encodeURIComponent(filter);
      keyword = encodeURIComponent(keyword);
      location.href = &#39;/admin/products?page=&#39; + page + &#39;&amp;filter=&#39; + filter + &#39;&amp;keyword=&#39; + keyword;
    } else {
      location.href = &#39;/admin/products?page=&#39; + page;
    }
  }
&lt;/script&gt;</code></pre>
</li>
</ul>
</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로젝트 4일차 회고]]></title>
            <link>https://velog.io/@kevin_kim/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-4%EC%9D%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@kevin_kim/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-4%EC%9D%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Wed, 23 Aug 2023 14:34:33 GMT</pubDate>
            <description><![CDATA[<h2 id="작업내용">작업내용</h2>
<hr>
<h3 id="1-회원관리-목록-기능-개발">1. 회원관리 목록 기능 개발</h3>
<ul>
<li>JPA를 활용하여 페이지네이션 처리</li>
<li>JPA를 활용한 페이지 네이션 추가</li>
</ul>
<h3 id="querydsl-추가-참고-블로그">QueryDsl 추가 참고 블로그</h3>
<p><a href="https://velog.io/@juhyeon1114/Spring-QueryDsl-gradle-%EC%84%A4%EC%A0%95-Spring-boot-3.0-%EC%9D%B4%EC%83%81">[Spring] QueryDsl gradle 설정 (Spring boot 3.0 이상)</a></p>
<p><a href="https://velog.io/@soyeon207/QueryDSL-Spring-Boot-%EC%97%90%EC%84%9C-QueryDSL-JPA-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0">Spring Boot 에서 QueryDSL JPA 사용하기</a></p>
<h3 id="2-회원관리-수정-기능-개발">2. 회원관리 수정 기능 개발</h3>
<ul>
<li>수정 시 닉네임하고 이메일은 중복되면 안됨</li>
<li>ajax를 통해 닉네임과 이메일이 다른 사람이 사용하고 있으면 submit을 막음</li>
</ul>
<h3 id="3-회원-삭제-기능-개발">3. 회원 삭제 기능 개발</h3>
<h3 id="🚀-trouble-shooting">🚀 Trouble Shooting</h3>
<ol>
<li>수정 시 form에 있는 값이 안넘어감<ul>
<li>문제 : 수정 시 form에 있는 값이 안넘어감</li>
<li>원인 : DTO에 @Builder를 사용하였더니 @Setter가 없어 값이 넘겨지지 않음</li>
<li>해결 : DTO에 @Setter 추가 해줌…</li>
</ul>
</li>
<li><strong>자바스크립트에서 submit이 안먹히는 현상</strong><ul>
<li>문제 : jquery로 form.submit()이 안되는 상황</li>
<li>원인 : 검색해보니 form 안에 submit이라는 아이디나 이름을 가지는 컴포넌트가 있다면 자바스크립트 안에서 form.submit()이 안되는 경우가 있다고 한다.<ul>
<li>찾아보니 버튼 id를 submit으로 해놨음..</li>
</ul>
</li>
<li>해결 : 아이디를 submitBnt로 변경</li>
</ul>
</li>
<li>delete 시 쿼리가 안나가고 조회만 됨<ul>
<li>문제 : delete 시 쿼리가 안나가고 조회만 됨</li>
<li>원인 : service에 @Transactional(readOnly = <em>true</em>) 를 해놨었음 ㅠㅠ</li>
<li>해결 : delete 메소드에 @Transactional 어노테이션 걸어줌</li>
</ul>
</li>
<li>delete 후 이상한 쿼리들이 나감<ul>
<li>문제 : delete 후 이상한 쿼리들이 나감</li>
<li>원인 : </li>
<li>해결 : 이부분은 아직 해결하지 못함... 추후에 리팩토링 때 해결할 예정!</li>
</ul>
</li>
</ol>
<p>오늘은 정말.. 안되는날이였다..
여기저기서 문제가 터지고 그 문제를 해결하기 위해서 계속 찾아보고 적용하기를 반복했던 하루였다..
그래도 다행히 오늘 해야할 것들을 모두 완료했다!
내일은 조금 무난히 넘어가길..ㅠㅠ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로젝트 3일차 회고]]></title>
            <link>https://velog.io/@kevin_kim/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-3%EC%9D%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@kevin_kim/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-3%EC%9D%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Wed, 23 Aug 2023 14:30:24 GMT</pubDate>
            <description><![CDATA[<h2 id="작업내용">작업내용</h2>
<hr>
<p>요즘 프로젝트 하면서 너무 정신이 없어 노션에만 적어놓고 블로그에 올리지 못했따..
다시 올려보자고..!!!</p>
<h3 id="1-관리자-회원관리-화면-개발">1. 관리자 회원관리 화면 개발</h3>
<h3 id="🚀-trouble-shooting">🚀 Trouble Shooting</h3>
<ol>
<li>부트스트랩 탬플릿을 활용하여 관리자 화면을 개발하여 spring boot에 붙였는데 css가 다 깨지는 현상<ul>
<li>문제 : 부트스트랩 탬플릿을 활용해서 화면 개발 후 spring boot에 붙였는데 css가 다 깨지는 현상</li>
<li>원인 : css가 tailwind여서 따로 설정을 해줘야햠</li>
<li>해결<ol>
<li><strong>Tailwind CSS 설치</strong> <ul>
<li>npm install tailwindcss postcss autoprefixer 명령어를 이용하여 tailwindcss 설치</li>
</ul>
</li>
<li><strong>설정 파일 생성</strong><ul>
<li>postcss.config.js을 생성하고 탬플릿에 사용하던 tailwind.config.js을 프로젝트 안으로 이동</li>
</ul>
</li>
<li>css 파일을 연결<ul>
<li>이때 경로를 /static/css/tailwind.css 이렇게 하면 인식을 못하기 때문에 
/css/tailwind.css로 설정해줘야함</li>
</ul>
</li>
</ol>
</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로젝트 2일차 회고]]></title>
            <link>https://velog.io/@kevin_kim/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-2%EC%9D%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@kevin_kim/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-2%EC%9D%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sat, 19 Aug 2023 15:13:24 GMT</pubDate>
            <description><![CDATA[<h3 id="엔티티-생성-및-연관관계-매핑">엔티티 생성 및 연관관계 매핑</h3>
<ul>
<li>엔티티 생성 시 Setter의 사용을 지양하고자 Builder를 사용</li>
</ul>
<h3 id="git-pullrequest">Git PullRequest</h3>
<ul>
<li><strong>Squash and Merge</strong><ul>
<li>feature 브랜치의 commit history를 합쳐서 깔끔하게 만들기 위해 사용</li>
<li>여러 개의 history가 하나로 합쳐져서 새로운 commit을 생성</li>
</ul>
</li>
</ul>
<h3 id="🚀-trouble-shooting">🚀 Trouble Shooting</h3>
<ol>
<li>RDS time_zone이 기본값인 UTC로 생성되어 있어 now()를 하게되면 현재 시간과 맞지 않음<ul>
<li>문제 : RDS time_zone이 기본값인 UTC로 생성되어 있어 now()를 하게되면 현재 시간과 맞지 않음</li>
<li>원인 : time_zone이 RDS 생성시 따로 설정해주지 않아 기본값으로 설정되어 있었음</li>
<li>해결 : 파라미터 그룹을 만들어서 time_zone을 Asia/Seoul로 변경 후 재부팅
<img src="https://velog.velcdn.com/images/kevin_kim/post/8a1a9dac-0808-4610-afb2-243aad0094ca/image.png" alt=""><img src="https://velog.velcdn.com/images/kevin_kim/post/67c56832-97dc-445d-9eae-5bca5e4f0583/image.png" alt=""></li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로젝트 회고 1일차]]></title>
            <link>https://velog.io/@kevin_kim/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0-1%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@kevin_kim/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0-1%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 17 Aug 2023 15:55:40 GMT</pubDate>
            <description><![CDATA[<h2 id="작업내용">작업내용</h2>
<hr>
<h3 id="github">Github</h3>
<ul>
<li>github Repository 생성</li>
<li>gitignore 변경</li>
<li>git branch 명명규칙 정리<ul>
<li>FEAT : 새로운 기능의 추가</li>
<li>FIX: 버그 수정</li>
<li>DOCS: 문서 수정</li>
<li>STYLE: 스타일 관련 기능(코드 포맷팅, 세미콜론 누락, 코드 자체의 변경이 없는 경우)</li>
<li>REFACTOR: 코드 리펙토링</li>
<li>TEST: 테스트 코트, 리펙토링 테스트 코드 추가</li>
<li>CHORE: 빌드 업무 수정, 패키지 매니저 수정(ex .gitignore 수정 같은 경우)</li>
</ul>
</li>
</ul>
<h3 id="aws">AWS</h3>
<ul>
<li>catdogpia AWS 공용계정 생성</li>
<li>RDS 생성<ul>
<li>DB는 MySQL</li>
<li>인바운드 규칙에 모든 IPv4가 접근 할 수 있도록 설정</li>
</ul>
</li>
</ul>
<h3 id="erd-변경">ERD 변경</h3>
<ul>
<li>파일 테이블은 파일과 파일 상세로 나눔</li>
<li>그 이유는 파일은 한번에 여러개 업로드가 가능하기 때문에 파일ID 하나의 여러개의 파일상세ID를 연결하기 위해 나눔</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kevin_kim/post/c327b619-0f52-456b-8022-8c11b237025e/image.png" alt=""></p>
<h3 id="applicationyml-파일-설정">application.yml 파일 설정</h3>
<ul>
<li>application.yml 파일에 db 접속 정보를 노출하지 않기 위해 applicaiton-secret.yml을 따로 생성함</li>
</ul>
<pre><code class="language-yaml"># application.yml 
spring:
  profiles:
    active: secret
  jpa:
    hibernate:
      ddl-auto: create
    properties:
      hibernate.format_sql: true

# application-secret.yml
spring:
  datasource:
    driver-class-name: DB 주소
    username: 유저네임
    password: 비밀번호</code></pre>
<h3 id="🚀-trouble-shooting">🚀 Trouble Shooting</h3>
<ol>
<li><p>RDS 엔드포인트를 가지고 팀원이 접속하려고 하면 접속이 되지 않음!</p>
<ul>
<li><p>문제 : RDS 엔드포인트를 가지고 팀원이 접속하려고 하면 접속이 되지 않음!</p>
</li>
<li><p>원인 : RDS를 퍼블릭으로 만들었지만 인바운드 규칙에서 MySQL을 내 IP로만 설정하게 해놓음..</p>
</li>
<li><p>해결 : IP를 모든 곳에서 접속할 수 있도록 변경함</p>
<p>  <img src="https://velog.velcdn.com/images/kevin_kim/post/4fbfa7e0-ab41-4f35-80c6-e5e7ec13bf35/image.png" alt=""></p>
</li>
</ul>
</li>
<li><p>application.yml에서 db 설정시 db access denied 발생</p>
<ul>
<li>문제 : application.yml에서 db 설정시 db access denied 발생!</li>
<li>원인 : MySQL 비밀번호 생성시 대문자 소문자 특수문자 포함 8자 이상으로 작성해야하는데 소문자로만 작성함</li>
<li>해결 : RDS에 들어가 대소문자 특수문자 포함해서 변경
<img src="https://velog.velcdn.com/images/kevin_kim/post/a87f5ba8-deba-4faa-8aef-070e359eb014/image.png" alt=""></li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[원티드 프리 온보딩 챌린지 Docker 1일차]]></title>
            <link>https://velog.io/@kevin_kim/%EC%9B%90%ED%8B%B0%EB%93%9C-%ED%94%84%EB%A6%AC-%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%B1%8C%EB%A6%B0%EC%A7%80-Docker-1%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@kevin_kim/%EC%9B%90%ED%8B%B0%EB%93%9C-%ED%94%84%EB%A6%AC-%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%B1%8C%EB%A6%B0%EC%A7%80-Docker-1%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Wed, 02 Aug 2023 03:10:49 GMT</pubDate>
            <description><![CDATA[<h1 id="docker란">Docker란?</h1>
<ul>
<li>컨테이너 기반 가상화 도구</li>
<li>애플리케이션을 컨테이너라는 단위로 격리하여 실행하는 기술</li>
<li>리눅스 컨테이너 기술인 LXC(Linux Containers)을 기반으로 한 기술</li>
<li>다양한 운영체제에서 사용할 수 있으며, 컨테이너화된 애플리케이션을 손쉽게 빌드, 배포, 관리할 수 있는 다양한 기능을 제공</li>
<li>위 기능들을 통해 애플리케이션을 빠르게 개발하고, 효율적으로 배포, 관리할 수 있음</li>
<li>컨테이너는 서로 격리되어 있지만 여러가지 방식을 통해 데이터를 공유하거나 통신 할 수 있음</li>
</ul>
<h2 id="container란">Container란?</h2>
<ul>
<li>컨테이너는 가상화 기술 중 하나</li>
<li>호스트 운영체제 위에 여러 개의 격리된 환경을 생성하여 각각의 컨테이너 안에서 애플리케이션을 실행</li>
</ul>
<h2 id="가상화virtualization-기술이란">가상화(Virtualization) 기술이란?</h2>
<ul>
<li>하나의 물리적인 컴퓨터 자원(CPU, 메모리, 저장장치 등)을 가상적으로 분할하여 여러 개의 가상 컴퓨터 환경을 만들어 내는 기술</li>
<li>이를 통해 물리적인 컴퓨터 자원을 더욱 효율적으로 사용할 수 있으며,서버나 애플리케이션 등을 운영하는데 있어 유연성과 안정성을 제공</li>
</ul>
<h3 id="도커를-사용하는-이유">도커를 사용하는 이유?</h3>
<ul>
<li>가상화는 필요하지만 가상머신은 각각의 Guest OS가 있기 때문에 무겁지만 도커는 Container Engine 을 통해 각각 Guest OS를 두지 않고 사용할 수 있어 VM 보다 더 가벼움</li>
<li>애플리케이션을 구동하기 위한 환경을 제공</li>
<li>코드 배포와 테스트를 쉽고 빠르게 진행가능</li>
</ul>
<h3 id="docker-architecture">Docker <strong>Architecture</strong></h3>
<p><img src="https://velog.velcdn.com/images/kevin_kim/post/dae765f9-0d7d-4b1a-9f10-261282511e99/image.png" alt=""></p>
<h3 id="도커-데몬docker-deamon--dockerd">도커 데몬(Docker deamon = dockerd)</h3>
<ul>
<li>도커 엔진의 핵심 구성 요소</li>
<li>도커 호스트에서 컨테이너를 관리하고 실행하는 역할</li>
<li>컨테이너를 생성, 시작, 중지, 삭제하는 등의 작업을 수행</li>
<li>컨테이너 이미지를 관리하고 외부에서 이미지를 다운로드하고 빌드하는 작업을 수행</li>
</ul>
<h3 id="도커-클라이언트docker-client">도커 클라이언트(Docker Client)</h3>
<ul>
<li>도커 사용자가 도커와 상호작용하는 기본방법</li>
<li><code>docker</code> 명령어를 사용하면  클라이언트가 명령을 API로서 도커 데몬으로 보내어 실행</li>
<li>이때 도커 클라이언트는 /var/run/docker.sock에 있는 유닉스 소켓을 통해 도커 데몬의 API를 호출</li>
<li>도커 클라이언트가 사용하는 유닉스 소켓은 같은 호스트 내에 있는 도커 데몬에게 명령을 전달할 때 사용</li>
</ul>
<h3 id="도커-레지스트리docker-registries">도커 레지스트리(Docker Registries)</h3>
<ul>
<li>도커 이미지를 관리하고 저장하는 곳<ul>
<li>Docker hub: 디폴트 레지스트리로 누구나 접근 가능한 공개형 저장소</li>
</ul>
</li>
</ul>
<h3 id="도커-오브젝트docker-object">도커 오브젝트(Docker Object)</h3>
<ul>
<li>도커 이미지<ul>
<li>도커 컨테이너를 만들기 위한 읽기 전용 템플릿</li>
</ul>
</li>
<li>도커 컨테이너<ul>
<li>한 도커 이미지의 실행 가능한 인스턴스</li>
<li>애플리케이션을 실행하기 위한 모든 파일과 설정 정보를 포함하는 패키지</li>
</ul>
</li>
</ul>
<aside>
💡 Docker hub는 App Store로 image는 App Store에서 받아온 프로그램(App), 그 프로그램을 실행시키면 발생하는 프로세스를 Container라고 생각하면 된다!
</aside>

<h2 id="도커-명령어">도커 명령어</h2>
<pre><code>- docker pull

```docker
docker pull [OPTIONS] NAME[:TAG|@DIGEST] 

ex) docker pull httpd
```

 - docker images

```docker
docker images
```

 - docker run

```docker
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

docker run httpd
docker run --name secondContainer httpd
docker run -p 8888:80 -v /Users/jk/wanted/2308/docker-pro-2308/lecture/1st:/usr/local/apache2/htdocs httpd
```

- **--** name ****
    - 컨테이너 이름을 설정
- -**d, --detach**
    - ****Detached 모드로 보통 데몬 모드라고 부르며, 컨테이너가 백그라운드로 실행
- **p, --publish**
    - 호스트와 컨테이너의 포트를 연결 (포트포워딩)
    - &lt;호스트 포트&gt;:&lt;컨테이너 포트&gt; → p 80:80
- **v, --volume**
    - 데이터 볼륨을 설정 호스트와 컨테이너의 디렉토리를 연결하여, 파일을 컨테이너에 저장하지 않호스트에 바로 저장 (마운트)

- docker stop

```docker
docker stop [OPTIONS] CONTAINER [CONTAINER...]

docker stop 9b0f49de746c
docker stop -a
```
 - docker logs

```docker
docker logs [OPTIONS] CONTAINER

docker logs second
docker logs second -f
```

- docker rm

```docker
docker rm [OPTIONS] CONTAINER [CONTAINER...]

docker rm 6026ab9b44cc
docker rm second -f
```
- docker rmi

```docker
docker rmi [OPTIONS] IMAGE [IMAGE...]
docker rmi 6026ab9b44cc
```</code></pre>]]></description>
        </item>
    </channel>
</rss>