<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>개발기록</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Fri, 19 Jan 2024 16:15:57 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>개발기록</title>
            <url>https://velog.velcdn.com/images/infinity-blue/profile/4aaaa5b6-0a6e-4ed3-8d75-64fd210acd44/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 개발기록. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/infinity-blue" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[RuntimeError: Failed to lock Pipfile.lock!]]></title>
            <link>https://velog.io/@infinity-blue/RuntimeError-Failed-to-lock-Pipfile.lock</link>
            <guid>https://velog.io/@infinity-blue/RuntimeError-Failed-to-lock-Pipfile.lock</guid>
            <pubDate>Fri, 19 Jan 2024 16:15:57 GMT</pubDate>
            <description><![CDATA[<p>가상환경을 만들기 위하여 <strong>pipenv --python &lt;&quot;version&quot;&gt;</strong>을 입력하였다. 미리 내가 만들어놓았던 requirements.txt가 인식되어 Pipfile이 자동으로 만들어졌고 이 pipfile을 이용하여  pipfile.lock을 만들기 위해 <strong>pipenv install</strong> 및 <strong>pipenv lock</strong>을 입력했다. 에러가 다음과 같이 뜬다.</p>
<img src=https://velog.velcdn.com/images/infinity-blue/post/3054d2fe-b8ef-4de9-b729-1751f7ea8604/image.png width=900 height=300>

<p>요약하자면 제목과 같은 오류인데, python과 pipenv 버전 사이의 호환성 문제같아 보인다.
python 3.7 기반의 가상환경을 만들었는데 오류에 3.8...이 나오길래 pipenv 버전과 파이썬 3.7 상호성에 문제가 있는지 공식문서를 확인해보았지만 문제는 없었다.
로컬과 가상환경의 Pipenv 버전을 각각 출력했더니 버전이 다르다.
로컬은 2023.10.20 인데 가상환경은 2023.11.15가 출력된다.
<img src=https://velog.velcdn.com/images/infinity-blue/post/6be7eaa3-9686-45f9-a540-0e6e90dcbe28/image.png width=700 height=300> <img src=https://velog.velcdn.com/images/infinity-blue/post/6948f36c-1a0c-4422-b600-98e06ffe25cc/image.png width=700 height=300></p>
<p>그래서 pip uninstall pipenv를 했더니 가상환경의 최신버전만 삭제되길래 직접 로컬버전의 경로를 입력해서 삭제하였다. 로컬과 가상환경의 pipenv버전을 통일시켰다.</p>
<p>우선. pipenv 경로확인 후 경로를 입력하여 아래와 같이 삭제.</p>
<pre><code>which pipenv
&lt;&quot;pipenv 설치경로&quot;&gt; uninstall pipenv </code></pre><p>기존의 가상환경을 삭제하고 새로운 가상환경을 생성해서 접속한 후 pipenv lock을 처음과같이 시도했더니 같은 오류가 떴다. 로컬과 가상환경의 파이썬과 pipenv 버전은 동일하다. 캐쉬 문제도 아니다. Pipenv 2023.11.15와 파이썬 3.7의 호환 문제도 아니다. 마지막으로, 가상환경에서 pipenv와 파이썬의 경로를 출력해보았다. <img src=https://velog.velcdn.com/images/infinity-blue/post/e803b168-9f25-4e2a-a5a8-76a432eedb8e/image.png width=900 height=300> 출처 경로가 다르다는 것을 알 수 있었다. 오류에서 떴었던 3.8이 pipenv의 경로에 확인된다.
가상환경에 접속되어 있는 상태로 pipenv경로를 변경하고자  재설치를 하였다.</p>
<pre><code>pip install pipenv</code></pre><p>pipenv와 python의 경로가 <strong>&#39;.../virtualenvs/..&#39;</strong>로 아래와 같이 통일되었다. 그리고 pipenv install을 입력하였더니 오류없이 pipfile.lock이 생성되었다. 문제가 해결되었다.  <img src= https://velog.velcdn.com/images/infinity-blue/post/4ef54eb4-c021-43aa-9a8f-dc2b33e24a09/image.png width=900 height=300> </p>
<h2 id="conclusion">Conclusion</h2>
<p>가상환경에서의 Pipenv와 Python의 경로가 같아야한다.
python 및 pipenv가 여러 개 설치되어 있지는 않은지, 어떤 버전을 사용하고 있는지 확인한다.</p>
<p><a href="https://pypi.org/project/pipenv/#history">pipenv 공식문서</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Pipenv] 가상환경 사용법]]></title>
            <link>https://velog.io/@infinity-blue/Pipenv-%EA%B0%80%EC%83%81%ED%99%98%EA%B2%BD-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@infinity-blue/Pipenv-%EA%B0%80%EC%83%81%ED%99%98%EA%B2%BD-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Thu, 18 Jan 2024 04:07:28 GMT</pubDate>
            <description><![CDATA[<h2 id="pip">Pip</h2>
<p>Python으로 작성된 패키지 소프트웨어를 설치 및 관리하는 패키지 관리 시스템</p>
<h2 id="pipenv">Pipenv</h2>
<p>Python 언어를 위한 가상환경 관리 도구로서 가상환경을 만들고 관리한다. Pipfile을 이용하여 프로젝트에 필요한 패키지 관리. </p>
<h4 id="1-pipenv-설치">1. pipenv 설치</h4>
<pre><code>pip install pipenv</code></pre><h4 id="2-가상환경-만들기">2. 가상환경 만들기</h4>
<pre><code>pipenv --python &lt;version number&gt;        </code></pre><h4 id="3-가상환경-실행">3. 가상환경 실행</h4>
<pre><code>pipenv shell</code></pre><h4 id="4-프로젝트의-패키지-설치-후-pipfile-및-pipfilelock-생성">4. 프로젝트의 패키지 설치 후 pipfile 및 pipfile.lock 생성.</h4>
<p><strong>1) requirements.txt가 존재하지 않는 경우.</strong>
-&gt; 가상환경에 직접 패키지 일일이 설치 후  requirements.txt를 생성. 
  그 다음, 바로 하단의 2) 명령어를 입력한다.  </p>
<pre><code>pipenv install &lt;package name&gt;
pip freeze &gt; requirements.txt</code></pre><p><strong>2) requirements.txt가 이미 존재하는 경우.</strong>
-&gt; requirements.txt에 적혀있는 패키지를 가상환경에 설치한 후 pipfile과 pipfile.lock을 생성.** <span style="color:#DF0101"><u>위의 3번과 같이 가상환경을 실행할 경우 이 단계가 필요없이 가상환경에서 requirements.txt를 알아서 인식하여 자동으로 pipfile을 만들기도 한다. 이 같은 경우로 진행될 경우, &quot;pipenv install&quot; 및 &quot;pipenv lock&quot;을 입력하여 pipfile.lock만 생성해주면 끝난다.</span></u>  **</p>
<pre><code>pipenv install -r requirements.txt</code></pre><p>상단의 내용은 pipenv 생성과 초기 세팅방법이다. 
나중에 패키지를 따로 더 설치할 시 pipenv.lock를 업데이트 해야 한다.</p>
<pre><code>pipenv install &lt;package name&gt;
pipenv lock</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[face-api] Uncaught (in promise) SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON"]]></title>
            <link>https://velog.io/@infinity-blue/face-api-Uncaught-in-promise-SyntaxError-Unexpected-token-DOCTYPE-...-is-not-valid-JSON</link>
            <guid>https://velog.io/@infinity-blue/face-api-Uncaught-in-promise-SyntaxError-Unexpected-token-DOCTYPE-...-is-not-valid-JSON</guid>
            <pubDate>Sun, 19 Nov 2023 09:46:48 GMT</pubDate>
            <description><![CDATA[<h2 id="problem">Problem</h2>
<p>React에서 face-api.js의 얼굴탐지 코드를 불러올 시 제목과 같은 에러가 뜬다.
에러를 일으키는 코드는 다음과 같다.</p>
<pre><code>  //Load Face-detection models from &#39;./public/models.&#39;
   const MODEL_URL = process.env.PUBLIC_URL + &#39;/models&#39;;

      Promise.all([
        faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL),
        faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL),
        faceapi.nets.faceRecognitionNet.loadFromUri(MODEL_URL),
        faceapi.nets.faceExpressionNet.loadFromUri(MODEL_URL),
      ]).then(setModelsLoaded(true));
    }</code></pre><h2 id="solution">Solution</h2>
<p>위와같이 깃헙에서 face-api를 다운로드 받지말고 face-api 깃헙 url를 아래와 같이 직접 입력한다.</p>
<h4 id="github의-json파일의-내용을-uri로-불러올-시-링크의-수정이-필요하다">github의 &#39;.json&#39;파일의 내용을 URI로 불러올 시 링크의 수정이 필요하다.</h4>
<p><strong>e.g.</strong></p>
<pre><code>github.com/..../face-api.js/blob/master/weights/..json</code></pre><h4 id="위의-uri를-다음과-같이-수정한다-하단의-솔루션-코드-참고">위의 URI를 다음과 같이 수정한다. 하단의 솔루션 코드 참고.</h4>
<pre><code>githubusercontent.com/..../face-api.js/master/weights/..json</code></pre><pre><code class="language-javascript">const detectionOptions = {
    withLandmarks: true,
    withDescriptors: true,
    minConfidence: 0.5,
    MODEL_URLS: {
      Mobilenetv1Model:
        &quot;https://raw.githubusercontent.com/ml5js/ml5-data-and-models/main/models/faceapi/ssd_mobilenetv1_model-weights_manifest.json&quot;,
      FaceExpressionModel:
        &quot;https://raw.githubusercontent.com/justadudewhohacks/face-api.js/master/weights/face_expression_model-weights_manifest.json&quot;,
      TinyFaceLandmarkModel:
        // &quot;https://raw.githubusercontent.com/justadudewhohacks/face-api.js/master/weights/face_landmark_68_model-weights_manifest.json&quot;,
        &quot;https://raw.githubusercontent.com/justadudewhohacks/face-api.js/master/weights/tiny_face_detector_model-weights_manifest.json&quot;,
      FaceLandmark68TinyNet:
        &quot;https://raw.githubusercontent.com/justadudewhohacks/face-api.js/master/weights/face_landmark_68_model-weights_manifest.json&quot;,
      FaceRecognitionModel:
        &quot;https://raw.githubusercontent.com/justadudewhohacks/face-api.js/master/weights/face_recognition_model-weights_manifest.json&quot;,
    },
  };

  useEffect(() =&gt; {
    const loadModels = async () =&gt; {
      try {
        await Promise.all([
          faceapi.nets.ssdMobilenetv1.loadFromUri(detectionOptions.MODEL_URLS.Mobilenetv1Model),
          faceapi.nets.tinyFaceDetector.loadFromUri(
            detectionOptions.MODEL_URLS.TinyFaceLandmarkModel
          ),
          faceapi.nets.faceLandmark68Net.loadFromUri(
            detectionOptions.MODEL_URLS.FaceLandmark68TinyNet
          ),
          faceapi.nets.faceRecognitionNet.loadFromUri(
            detectionOptions.MODEL_URLS.FaceRecognitionModel
          ),
          faceapi.nets.faceExpressionNet.loadFromUri(
            detectionOptions.MODEL_URLS.FaceExpressionModel
          ),
        ]);</code></pre>
<br>

<p>만약 상단처럼 코드를 수정했음에도 같은 오류가 뜨는 경우가 있다. 한번에 여러개의 모델을 불러올 시 리소스 로드에 과부하가 걸려서 그런 듯 싶다. 이럴 땐 promise.all 대신 하단처럼 모델을 각각 불러오면 해결된다.</p>
<pre><code>    // &#39;prommise.all&#39; often makes loading stucked due to heavy resource load. try to load models one by one.
    await faceapi.nets.tinyFaceDetector.loadFromUri(
      detectionOptions.MODEL_URLS.TinyFaceLandmarkModel
    );
    // extracting data like face position, shape, and texture.
    await faceapi.nets.faceLandmark68Net.loadFromUri(
      detectionOptions.MODEL_URLS.FaceLandmark68TinyNet
    );
    // comparing facial features with existing data from the database.
    await faceapi.nets.faceRecognitionNet.loadFromUri(
      detectionOptions.MODEL_URLS.FaceRecognitionModel
    );
    await faceapi.nets.faceExpressionNet.loadFromUri(
      detectionOptions.MODEL_URLS.FaceExpressionModel
    );</code></pre><p><br></br></p>
<h3 id="references">References</h3>
<p><a href="https://github.com/justadudewhohacks/face-api.js/tree/master/weights">face-api.js</a>
<a href="https://stackoverflow.com/questions/76404973/facial-recognition-in-the-browser-using-face-api-js-not-working">How to load face-api.js</a>
<a href="https://justadudewhohacks.github.io/face-api.js/docs/index.html#interface-face-detection">How to use face-api.js</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ERROR: Could not build wheels for dlib]]></title>
            <link>https://velog.io/@infinity-blue/ERROR-Could-not-build-wheels-for-dlib</link>
            <guid>https://velog.io/@infinity-blue/ERROR-Could-not-build-wheels-for-dlib</guid>
            <pubDate>Tue, 07 Nov 2023 05:31:51 GMT</pubDate>
            <description><![CDATA[<h2 id="problem">PROBLEM</h2>
<p>도커 이미지를 만드는 데 다음과 같은 오류가 뜬다.:</p>
<pre><code>Tons of lines of errors. But the below is a parts of them:
79.55 [pipenv.exceptions.InstallError]:       [end of output]
79.55 [pipenv.exceptions.InstallError]:   
79.55 [pipenv.exceptions.InstallError]:   note: This error originates from a subprocess, and is likely not a problem with pip.
79.55 [pipenv.exceptions.InstallError]:   ERROR: Failed building wheel for dlib
79.55 [pipenv.exceptions.InstallError]: ERROR: Could not build wheels for dlib, which is required to install pyproject.toml-based projects
79.55 ERROR: Couldn&#39;t install package: {}
79.55  Package installation failed...
79.56 /usr/local/lib/python3.7/subprocess.py:883: ResourceWarning: subprocess 50 is still running
79.56   ResourceWarning, source=self)
79.56 ResourceWarning: Enable tracemalloc to get the object allocation traceback
79.56 sys:1: ResourceWarning: unclosed file &lt;_io.TextIOWrapper name=4 encoding=&#39;utf-8&#39;&gt;
79.56 ResourceWarning: Enable tracemalloc to get the object allocation traceback
79.56 sys:1: ResourceWarning: unclosed file &lt;_io.TextIOWrapper name=7 encoding=&#39;utf-8&#39;&gt;
79.56 ResourceWarning: Enable tracemalloc to get the object allocation traceback
------
failed to solve: process &quot;/bin/sh -c cd /app/Backend &amp;&amp; pipenv install --deploy&quot; did no</code></pre><br/>

<h2 id="troubleshooting-steps">Troubleshooting steps:</h2>
<p>오류 메시지에 dlib이 있는 걸 보니 dlib이 실행되기 위해서 도커 이미지 빌드 시에 반드시 설치해야 할 패키지가 있는 듯 싶다.</p>
<h2 id="solutions">Solutions:</h2>
<p>하단의 코드를 Dockerfile에 추가한다. </p>
<pre><code>RUN apt-get update &amp;&amp; \
    apt-get install -y \
    build-essential \
    cmake \
    libopenblas-dev \
    liblapack-dev \
    libx11-dev \
    libgtk-3-dev \
    python3-dev \
    python3-pip \
    python3-numpy \
    &amp;&amp; rm -rf /var/lib/apt/lists/*</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Nginx] SSL Connection refused.]]></title>
            <link>https://velog.io/@infinity-blue/Nginx-SSL-Connection-refused</link>
            <guid>https://velog.io/@infinity-blue/Nginx-SSL-Connection-refused</guid>
            <pubDate>Mon, 06 Nov 2023 17:58:08 GMT</pubDate>
            <description><![CDATA[<h1 id="nginx란">Nginx란?</h1>
<p>*<em>리버스 프록시의 역할과 정적파일을 처리하는 웹 서버. *</em></p>
<p><strong>리버스 프록시란?</strong>
외부 클라이언트에서 서버로 접근 시, 내부 서버로 접근할 수 있게 해주는 중개자 역할. </p>
<h2 id="problem">Problem</h2>
<p>VM에 백엔드 서버가 돌아가고 있다. 백엔드 서버를 SSL화 하기 위하여 Nginx와 Letsencrypt를 사용하였다. nginx와 Letsencrypt를 VM에 설치하고 실행하였지만 포트 충돌이 발생하였다. 결국 nginx를 VM에 직접 설치하는 게 아닌 Nginx가 도커화 된 이미지를 VM에 직접 실행시키기로 하였다. Nginx 이미지가 Letsencrypt의 SSL 인증서를 인식하여 SSL 설정을 하기까지 제목과 같은 오류가 떴고 삽질과정과 해결방법은 다음과 같다:</p>
<br/>

<h2 id="troubleshooting-process--solution">Troubleshooting process &amp; Solution</h2>
<p>우선, VM의 네트워크 방화벽 설정에는 문제가 없다는 걸 확인 후 &#39;nginx -t&#39;로 syntax오류를 점검하였지만 정상이었다. &#39;error.log&#39;를 출력하고자 하였지만 아무것도 뜨지 않았다. SSL 점검해주는 온라인 사이트에 주소를 입력한 결과, SSL 인증서를 nginx가 전혀 인식하지 못하는 걸 발견하였다. nginx 설정 코드와 SSL인증서 인식의 문제로 보인다. 
<a href="https://www.ssllabs.com/ssltest/">SSL Test tool</a></p>
<h4 id="1-letsencrypt-ssl은--etcletsencryptliveyour_domain_name에-저장되어-있다-ls--la으로-symbolic-link를-확인-결국-파일-하나하나를-복붙하였다">1. Letsencrypt SSL은  &#39;/etc/letsencrypt/live/your_domain_name/&#39;에 저장되어 있다. ls -la으로 symbolic link를 확인. 결국 파일 하나하나를 복붙하였다.</h4>
<pre><code>sudo cp /etc/letsencrypt/archive/your_domain_name/cert1.pem /etc/docker_certs/
sudo cp /etc/letsencrypt/archive/your_domain_name/privkey1.pem /etc/docker_certs/
sudo cp /etc/letsencrypt/archive/your_domain_name/chain1.pem /etc/docker_certs/
sudo cp /etc/letsencrypt/archive/your_domain_name/fullchain1.pem /etc/docker_certs/</code></pre><h4 id="2-docker-composeyml의-volumes에-위의-경로를-마운트한다-도커-이미지-실행-시-도커가-컨테이너-내부에-디렉토리를-의-오른쪽-경로와-같이-만들고--왼쪽의-경로에-존재하는-데이터를-도커가-만든-그-경로에-복붙하겠다는-뜻이다-오른쪽의-경로는-dockerfile에-설정해-놓은-경로이다-docker-이미지가-실행될-때-만들어질-오른쪽의-경로에-etcdocker_certs의-내용을-복사한다">2. &#39;docker-compose.yml&#39;의 &#39;Volumes:&quot;에 위의 경로를 마운트한다. 도커 이미지 실행 시 도커가 컨테이너 내부에 디렉토리를 &#39;:&#39;의 오른쪽 경로와 같이 만들고 &#39;:&#39; 왼쪽의 경로에 존재하는 데이터를 도커가 만든 그 경로에 복붙하겠다는 뜻이다. 오른쪽의 경로는 Dockerfile에 설정해 놓은 경로이다. Docker 이미지가 실행될 때 만들어질 오른쪽의 경로에 &#39;/etc/docker_certs&#39;의 내용을 복사한다.</h4>
<pre><code>/etc/docker_certs:/etc/nginx/ssl</code></pre> <br/>

<h4 id="3-ssl파일이-위의-경로에-존재하는지-확인한다">3. SSL파일이 위의 경로에 존재하는지 확인한다.</h4>
<pre><code>docker exec -it &lt;nginx-container-name&gt; ls -l /etc/docker_certs</code></pre> <br/>

<h4 id="4-nginx-설정과-관련된-코드를-의심한-만큼-nginx-컨테이너의-defaultconf파일을-확인한다-nginx를-설치할-때-기본으로-설정되어-있는-코드-외에-ssl-코드는-없었다">4. nginx 설정과 관련된 코드를 의심한 만큼 nginx 컨테이너의 default.conf파일을 확인한다. nginx를 설치할 때 기본으로 설정되어 있는 코드 외에 SSL 코드는 없었다.</h4>
<pre><code>docker exec -it &lt;nginx-container-name&gt; cat /etc/nginx/conf.d/default.conf</code></pre><h4 id="5-defaultconf-외의-ssl코드가-있을만한-conf파일을-찾던-과정에-nginxconf파일을-발견하였다-include에-있는-confd-디렉토리에-ssl코드가-들어있는지-확인한다">5. default.conf 외의 ssl코드가 있을만한 .conf파일을 찾던 과정에 &#39;nginx.conf&#39;파일을 발견하였다. &#39;include...&#39;에 있는 &#39;../conf.d/&#39; 디렉토리에 SSL코드가 들어있는지 확인한다.</h4>
<pre><code>include /etc/nginx/conf.d/*.conf;</code></pre> <br/>

<h4 id="6-ssl코드가-들어있는-파일이-없다-docker-composeyml에-nginx-설정관련-코드의-볼륨-마운트가-없었기에-nginx-container-내부에-ssl설정-코드가-존재하지-않다는-걸-깨달았다-server블락이-들어있도록-미리-커스텀-해놓았던-vm-홈-디렉토리에-있는-nginxconf를-docker-composeyml에-추가하였다">6. SSL코드가 들어있는 파일이 없다. docker-compose.yml에 nginx 설정관련 코드의 볼륨 마운트가 없었기에 nginx container 내부에 SSL설정 코드가 존재하지 않다는 걸 깨달았다. Server블락이 들어있도록 미리 커스텀 해놓았던 VM 홈 디렉토리에 있는 &#39;nginx.conf&#39;를 &#39;docker-compose.yml&#39;에 추가하였다.</h4>
<pre><code>...
services:
  nginx:
    volumes:
     ./nginx.conf:/etc/nginx/nginx.conf</code></pre><h4 id="7-컨테이너를-실행시켜-보니-nginxconf파일에-server-블락이-존재해서는-안-된다고-나온다-이를-해결하기-위하여-server-블락을-옮겨놓을-tryonconf-파일을-vm-홈-디렉토리에-새로-만든다nginxconf파일의-server-블락을-tryonconf에-복사한다">7. 컨테이너를 실행시켜 보니 &#39;./nginx.conf&#39;파일에 &#39;server&#39; 블락이 존재해서는 안 된다고 나온다. 이를 해결하기 위하여 &#39;server&#39; 블락을 옮겨놓을 &#39;tryon.conf&#39; 파일을 VM 홈 디렉토리에 새로 만든다.&#39;nginx.conf&#39;파일의 &#39;Server&#39; 블락을 tryon.conf에 복사한다.</h4>
<p><strong>tryon.conf</strong></p>
<pre><code>server {
    # Redirect all HTTP traffic to HTTPS
    listen 80;
    server_name &lt;your_domain_name_or_IP&gt;;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name &lt;your_domain_name_or_IP&gt;;

    ssl_certificate /etc/nginx/ssl/fullchain1.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey1.pem;

    # Strong SSL Settings
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers &#39;TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384&#39;;
    ssl_prefer_server_ciphers off;
    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
    ssl_session_tickets off;

    # Add headers to serve security related headers
    add_header Strict-Transport-Security &quot;max-age=63072000&quot; always;
    add_header X-Content-Type-Options &quot;nosniff&quot; always;
    add_header X-Frame-Options &quot;DENY&quot; always;
    add_header X-XSS-Protection &quot;1; mode=block&quot; always;
    # Forward all requests to Django application running in a Docker container named &#39;django&#39; on port 8000.
    location / {
        # add_header &#39;Access-Control-Allow-Origin&#39;.
        proxy_pass http://&lt;&quot;service name in docker-compose.yml&quot;&gt;:8000; //One of services in docker-compose.yml
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    # The path inside the Nginx container to the static files.
    location /static/ {
        alias /usr/share/nginx/html/static/;
    }
    # The path inside the Nginx container to the media files. 
    location /media/ {
        alias /usr/share/nginx/html/media/;
    }
}</code></pre><h4 id="8-docker-composeyml에-ssl관련-코드가-들어있는-tryonconf를-볼륨-마운트한다-아래-코드의-마운트를-쉽게-설명하자면-span-stylecolordf0101u현재경로에-있는-tryonconf-란-서브-디렉토리의-내용물을-컨테이너를-생성할-때-컨테이너의-내부경로인-etcnginxconfdtryonconf에-복사붙여넣기-하겠다는-말이다uspan">8. docker-compose.yml에 ssl관련 코드가 들어있는 tryon.conf를 볼륨 마운트한다. 아래 코드의 &quot;마운트&quot;를 쉽게 설명하자면 <span style="color:#DF0101"><u>현재경로에 있는 tryon.conf 란 서브 디렉토리의 내용물을 컨테이너를 생성할 때 컨테이너의 내부경로인 &#39;/etc/nginx/conf.d/tryon.conf&#39;에 복사붙여넣기 하겠다는 말이다.</u></span></h4>
<pre><code>...
  nginx:
    volumes:
      - ./tryon.conf:/etc/nginx/conf.d/tryon.conf</code></pre><p>이로서, Nginx를 설치할 때 기본으로 깔려있는 &#39;nginx.conf&#39;에 &#39;include /etc/nginx/conf.d/*.conf&#39;가 존재하기에 nginx 컨테이너를 실행할 시 &#39;/etc/nginx/conf.d/tryon.conf&#39;가 적용된다. 
이로서 SSL인증 문제가 해결되었다..
<br/><br/></p>
<h3 id="conclusion">Conclusion</h3>
<p>도커 컨테이너 실행 시 볼륨 마운트는 컨테이너 내부구조 구축과 그에 따른 SSL인증을 위해서도 아주 중요하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA['ModuleNotFoundError: No module named '<project name>'.]]></title>
            <link>https://velog.io/@infinity-blue/ModuleNotFoundError-No-module-named-project-name</link>
            <guid>https://velog.io/@infinity-blue/ModuleNotFoundError-No-module-named-project-name</guid>
            <pubDate>Mon, 06 Nov 2023 08:08:00 GMT</pubDate>
            <description><![CDATA[<h2 id="problem">Problem</h2>
<p> 도커 이미지를 실행 후 docker logs 를 출력하였더니 제목과 같은 에러가 뜬다.
 &#39;docker-compose.yml&#39;, Dockerfile은 다음과 같다.</p>
<h4 id="dockerfile">Dockerfile</h4>
<pre><code> ...
  services:
    django:
     volumes:
      - .:/app
      - tryon_static_volume:/app/static
      - tryon_media_volume:/app/media
    nginx:
     volumes:
      - tryon_static_volume:/app/static
      - tryon_media_volume:/app/media
      - /etc/docker_certs:/etc/nginx/ssl
      ...</code></pre><h4 id="docker-composeyml">docker-compose.yml</h4>
<pre><code>...
# Set the working directory inside the Django container.
WORKDIR /app

# Install system dependencies
RUN apt-get update
..
# Copy the requirements file to the container
COPY requirements.txt /app/

# Install Python dependencies
RUN pip install -r requirements.txt

# Copy the Django application files to the container
COPY . /app/

# Set the working directory to where manage.py is located
WORKDIR /app/Backend/tryon

# Set a default port in case one isn&#39;t provided
ENV PORT=8000
# Collect static files
RUN python manage.py collectstatic --noinput

# Document that the service listens on port 8000.
EXPOSE 8000  

# Start the application
CMD gunicorn tryon.wsgi:application --bind 0.0.0.0:$PORT</code></pre><h2 id="troubleshooting-steps">Troubleshooting steps</h2>
<p>settings.py의 &#39;INSTALLED_APPS&#39; 부분에 장고 프로젝트 이름이 동일하게 설정되어 있는지 확인하였다. 또한, &#39;requirements.txt&#39; 대신 &#39;pipfile&#39;을 사용하여 파이썬 의존성을 설치하였다. 문제를 해결하는 도중 발생한 별개의 오류에 대해선 아래의 링크에 정리를 해 놓았다. 
<a href="https://velog.io/@infinity-blue/ERROR-Could-not-build-wheels-for-dlib">Dlib error</a></p>
<h2 id="solution">Solution</h2>
<p>Python이 장고 프로젝트를 인식 못하는 문제이다. 
Dockerfile 을 아래와 같이 수정하였다. 장고 프로젝트 파일을 컨테이너에 복사하고 장고 프로젝트 파일의 위치를 알려주기 위하여 PYTHONPATH 변수를 아래와 같이 추가하였다. 또한 &#39;Pipenv  run..&#39;을 사용하여 이 변수가 적용된 환경에서 프로젝트를 실행하도록 하였다. </p>
<h4 id="docker-composeyml-1">docker-compose.yml</h4>
<pre><code> ...
FROM python:3.7-slim

# Install Pipenv
RUN pip install pipenv

# Copy Pipfile and Pipfile.lock
COPY Backend/Pipfile /app/Backend/
COPY Backend/Pipfile.lock /app/Backend/

# Install Python dependencies from Pipfile into the container&#39;s environment.
# &#39;--deploy&#39; ensures that &#39;Pipfile.lock&#39; is up to date.
RUN cd /app/Backend &amp;&amp; pipenv install --deploy

# Copy the Django application files to the container
COPY Backend/tryon /app/Backend/tryon

# Set environment variables so that Python can find Django project.
# When &#39;pipenv run..&#39; starts app as below, the variables should be already set and applied. 
ENV PORT=8000 \
    PYTHONPATH=/app/Backend/tryon \
    DJANGO_SETTINGS_MODULE=tryon.settings

# Document that the service listens on port 8000.
EXPOSE 8000  

# Set the working directory to where manage.py is located
# WORKDIR /app/Backend/tryon

# Collect static files
RUN cd /app/Backend/tryon &amp;&amp; pipenv run python manage.py collectstatic --noinput

# Start the application. Ensure that the application is accessible from outside the container.
# CMD is executed once container is started from the resulting image.
# &#39;Pipenv run&#39; runs the command in the context of the environment set up by &#39;pipenv.&#39;
CMD cd /app/Backend/tryon &amp;&amp; pipenv run gunicorn tryon.wsgi:application --bind 0.0.0.0:$PORT

</code></pre><p> Dockerfile</p>
<pre><code>...
 volumes:
      - .:/app
      # The path on the right is set based on where Django files are copied to the container, for which Dockerfile is reponsible.
      - tryon_static_volume:/app/Backend/tryon/static
      - tryon_media_volume:/app/Backend/tryon/media</code></pre><p><br><br/></p>
<h2 id="follow-up-actions">Follow-Up Actions</h2>
<p>워낙 장고 프로젝트 디렉토리가 복잡하다 보니 구조가 헷갈려서 경로를 잘못 입력하곤 했었다.
다음부터는 프로젝트 디렉토리 구조를 미리 문서화 해야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Docker & GC VM] 기본 사용법]]></title>
            <link>https://velog.io/@infinity-blue/Docker-GC-VM-%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@infinity-blue/Docker-GC-VM-%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Tue, 24 Oct 2023 15:05:23 GMT</pubDate>
            <description><![CDATA[<p>도커의 기본적인 최소한의 명령어를 Google Cloud VM에서 사용하는 방법에 관하여 알아보자. 어떤 환경에서 도커 이미지를 사용하느냐에 따라 코드가 조금 달라질 뿐 (e.g. &quot;gcr.io..&quot;) 전반적인 명령어 사용법은 거의 같다.  </p>
<h2 id="도커란">도커란?</h2>
<p><strong>애플리케이션과 그에 필요한 모든 것(e.g.환경설정, 라이브러리, 시스템 툴, 등) 을 패키징하여 어느 환경이든 개발, 배포 및 구동을 쉽게 하도록 도와주는 툴.</strong></p>
<h3 id="docker-실행하기아래의-2가지-방법-모두-dockerfile은-필수">Docker 실행하기(아래의 2가지 방법 모두 Dockerfile은 필수)</h3>
<h4 id="1-도커-이미지-만들기-는-현재-위치한-디렉토리-위치에서-도커파일을-찾게-한다">1. 도커 이미지 만들기 (&quot;.&quot;는 현재 위치한 디렉토리 위치에서 도커파일을 찾게 한다.)</h4>
<p>*<em>1) *</em> Dockerfile만을 이용한 대표적인 이미지 빌드 방식. </p>
<pre><code>docker build -f &lt;도커파일 이름&gt; -t &lt;원하는 도커 이미지 이름&gt; .
</code></pre><ul>
<li>Dockerfile 예시(예시 백엔드는 장고):<pre><code>FROM python:3.7-slim
</code></pre></li>
</ul>
<h1 id="set-the-working-directory-inside-the-container">Set the working directory inside the container.</h1>
<p>WORKDIR /app</p>
<h1 id="install-system-dependencies">Install system dependencies</h1>
<p>RUN apt-get update
RUN apt install...
RUN apt-get install -y cmake</p>
<h1 id="install-pipenv">Install Pipenv.</h1>
<p>RUN pip install pipenv</p>
<h1 id="copy-pipfile-and-pipfilelock">Copy Pipfile and Pipfile.lock</h1>
<p>COPY.. /app/Backend/</p>
<p>RUN .....</p>
<h1 id="document-that-the-service-listens-on-port-8000">Document that the service listens on port 8000.</h1>
<p>EXPOSE 8000  </p>
<h1 id="set-the-working-directory-to-where-managepy-is-located">Set the working directory to where manage.py is located</h1>
<h1 id="workdir-appbackend">WORKDIR /app/Backend/..</h1>
<h1 id="collect-static-files">Collect static files</h1>
<p>RUN cd /app/Backend/..&amp;&amp; pipenv run python manage.py collectstatic --noinput</p>
<h1 id="set-a-default-port-in-case-one-isnt-provided">Set a default port in case one isn&#39;t provided</h1>
<h1 id="env-port8000">ENV PORT=8000</h1>
<h1 id="start-the-application-ensure-that-the-application-is-accessible-from-outside-the-container">Start the application. Ensure that the application is accessible from outside the container.</h1>
<p>CMD cd /app/Backend/..&amp;&amp; pipenv run gunicorn ..wsgi:application --bind 0.0.0.0:$PORT</p>
<pre><code>
**2) docker-compose.yml** 을 만들어 아래의 예시와 같이 어떤 서비스를 어떻게 빌드할 지 코드를 짠 후 아래의 명령어를 입력한다. 다수의 컨테이너를 만들경우 추천.</code></pre><p>docker-compose build &lt;docker-compose.yml의 services 중 하나&gt;</p>
<pre><code>+ docker-compose.yml 예시:</code></pre><p>version: &#39;3&#39;
services:
  django:
    build:
      context: .
      dockerfile: Dockerfile
    image: ......
    expose:
      - &quot;8000&quot;
    ports:
      - &quot;8000:8000&quot;
    volumes:
    # mount &#39;tryon_..&#39; from this project to &#39;/app/..&#39; inside the container. The work directory of container is defined Dockerfile. 
      - tryon_static_volume:/app/..
      - tryon_media_volume:/app/..
        # environment:
        # - PYTHONPATH=/app/Backend
        # - DJANGO_SETTINGS_MODULE=project.project.settings
  nginx:
    build:
      context: .
      dockerfile: Dockerfile-nginx
    image: .....
    ports:
      - &quot;80:80&quot;
      - &quot;443:443&quot;
    volumes:
      - ...static_volume:/usr/share/nginx/html/static
      - ...media_volume:/usr/share/nginx/html/media
      ....
volumes:
  tryon_static_volume:
  tryon_media_volume:</p>
<pre><code>위의 docker-compose.yml의 경우 &#39;Volumes&#39;를 설정해야 컨테이너의 파일 시스템을 별개의 컨테이너 및 로컬 호스트 간에 공유할 수 있다.

#### 2. 도커 이미지 태그하기</code></pre><p>docker tag &lt;위에서 만든 이미지 이름&gt; &quot;gcr.io/&lt;&quot;project ID&quot;&gt;/&lt;&quot;태그하고자 하는 도커 이미지 이름&quot;&gt;:&lt;&quot;이미지 식별 버전&quot;&gt;</p>
<pre><code>&lt;br/&gt;

#### 3. VM에 SSH 접속.
#### 4. Google cloud의 Container registry에 있는 도커 이미지를 가져온다.</code></pre><p>docker pull gcr.io/&lt;&quot;project ID&quot;&gt;/&lt;&quot;도커 이미지 이름&quot;&gt;:&lt;&quot;이미지 식별 버전&quot;&gt;</p>
<pre><code>
#### 5. 도커 이미지 실행시키기.
**1) Dockerfile 만을 이용하여 빌드된 이미지일 경우**</code></pre><p>docker run -d --name &lt;&quot;원하는 도커 컨테이너 이름&quot;&gt; gcr.io/&lt;&quot;project ID&quot;&gt;/&lt;&quot;도커 이미지 이름&quot;&gt;:&lt;&quot;이미지 식별 버전&quot;&gt;</p>
<pre><code>**2) docker-compose.yml, Dockerfile을 이용하여 만들어진 이미지일 경우**</code></pre><p>docker-compose up -d  </p>
<pre><code>**OR**</code></pre><p>docker-compose up &lt;docker-compose.yml에 짜여진 서비스 이름&gt; -d</p>
<pre><code></code></pre><p>docker-compose up --build -d </p>
<pre><code>#### 6. 실행되고 있는 도커 컨테이너 중단.</code></pre><p>docker stop &lt; 컨테이너 이름&gt; </p>
<pre><code></code></pre><p>docker-compose down</p>
<pre><code>&lt;br/&gt;

---
### 그 외 Docker 명령어  
#### 6. 컨테이너 로그 확인</code></pre><p>docker logs <container_id_or_name></p>
<pre><code>#### 7. 현재 돌아가고 있는 컨테이너 프로세스 리스트의 상태 출력</code></pre><p>docker ps </p>
<pre><code>#### 8. 오류로 인해 돌아가지 않는 컨테이너 리스트 출력</code></pre><p>docker ps -a</p>
<pre><code>#### 7. 컨데이터에 접속하기. </code></pre><p>docker exec -it <container_name_or_id> /bin/sh
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[VM & Docker] 외부 ip로 VM접속 불가]]></title>
            <link>https://velog.io/@infinity-blue/GCE-VM-%EC%99%B8%EB%B6%80-ip%EB%A1%9C-VM%EC%A0%91%EC%86%8D-%EB%B6%88%EA%B0%80</link>
            <guid>https://velog.io/@infinity-blue/GCE-VM-%EC%99%B8%EB%B6%80-ip%EB%A1%9C-VM%EC%A0%91%EC%86%8D-%EB%B6%88%EA%B0%80</guid>
            <pubDate>Tue, 24 Oct 2023 11:34:58 GMT</pubDate>
            <description><![CDATA[<h2 id="problem">Problem</h2>
<p> Django 서버와 Nginx를 도커 컨데이너화 하여 GCE(Google Compute Engine) VM에 호스팅 하였다. 그러나 VM 외부 IP주소로 서버 접속이 불가하였다.</p>
<h2 id="troubleshooting--solution">Troubleshooting &amp; Solution</h2>
<h4 id="1-docker-컨테이너가-돌아가고-있는-지-확인">1. Docker 컨테이너가 돌아가고 있는 지 확인.</h4>
<pre><code> docker ps</code></pre><h4 id="2-도커-로그-확인">2. 도커 로그 확인.</h4>
<pre><code>docker logs &lt;  도커 컨테이너 이름 &gt;</code></pre><h4 id="3----nginxconf-코드-재검토----syntax-error가-없음을-확인">3    nginx.conf 코드 재검토 -&gt;  syntax error가 없음을 확인.</h4>
<pre><code> nginx -t</code></pre><h4 id="4----firewall의-httphttps-포트가-docker-컨테이너의-포트와-같은지-확인">4    Firewall의 HTTP/HTTPS 포트가 Docker 컨테이너의 포트와 같은지 확인.</h4>
<h4 id="5----firewall-http와-도커의-포트를-8000에서-80으로-변경">5    Firewall HTTP와 도커의 포트를 8000에서 80으로 변경.</h4>
<h4 id="6-cannot-be-reached-오류는-사라졌지만-welcome-to-nginx-메시지만-뜸">6 &quot;Cannot be reached&quot; 오류는 사라졌지만 &quot;Welcome to Nginx&quot; 메시지만 뜸.</h4>
<h4 id="7-장고와-nginx-도커-컨데이너가-같은-네트워크-상에-있음을-확인">7 장고와 Nginx 도커 컨데이너가 같은 네트워크 상에 있음을 확인.</h4>
<pre><code> - docker network ls
 - docker network inspect &lt; network name &gt;</code></pre><p>   -&gt; <strong>&quot;bridge&quot;라는 같은 네트워크 상에 존재함을 확인.</strong></p>
<h4 id="8-vm의-도커-컨테이너-내부에-접속">8. VM의 도커 컨테이너 내부에 접속.</h4>
<pre><code>docker exec -it &lt; container name &gt; /bin/bash</code></pre><br/>

<h4 id="9-etcnginxconfd-폴더를-확인한-결과-내가-만든-nginxconf-파일내용이-보이지-않음-defaultconf라는-기본-설정된-코드만-보인다-nginxconf-파일이-제대로-적용되지-않은-듯-하다">9. &quot;/etc/nginx/conf.d/&quot; 폴더를 확인한 결과 내가 만든 &quot;nginx.conf&quot; 파일내용이 보이지 않음. &quot;default.conf&quot;라는 기본 설정된 코드만 보인다. &quot;nginx.conf&quot; 파일이 제대로 적용되지 않은 듯 하다.</h4>
<h4 id="10-defaultconf를-다른-이름으로-수정하고-nginxconf의-코드를-새로-만든-defaultconf에-복사-후-붙여넣기-하였다">10. &quot;default.conf&quot;를 다른 이름으로 수정하고 &quot;nginx.conf&quot;의 코드를 새로 만든 &quot;default.conf&quot;에 복사 후 붙여넣기 하였다.</h4>
<h4 id="11-nginx--t로-syntax-error-수정-후-새로고침을-함으로서-문제를-해결하였다">11. &quot;nginx -t&quot;로 syntax error 수정 후 새로고침을 함으로서 문제를 해결하였다.</h4>
<pre><code>nginx -s reload</code></pre><br/>

<h2 id="conclusion">Conclusion</h2>
<p><strong>&quot;Welcome to Nginx&quot;</strong> 메시지가 화면에 보일 시 본인이 커스텀화 해놓은 nginx 설정 파일이 기본으로 nginx에 내재되어 있는 &quot;default.conf&quot;를 오버라이딩 하지 못함으로서 일어나는 문제다. 
<strong>-&gt; &quot;default.conf&quot;를 수정.</strong>
<strong>&quot;Cannot be reached&quot;</strong> 로 접속이 아예 불가할 시 네트워크 설정을 재검토하자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Github] 기본 사용법]]></title>
            <link>https://velog.io/@infinity-blue/Github-%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@infinity-blue/Github-%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Tue, 10 Oct 2023 11:38:23 GMT</pubDate>
            <description><![CDATA[<p>모든 프로젝트를 혼자 진행하다 보니 Git을 사용할 필요성을 못 느껴서 로컬 PC 에 
여러 버전을 저장하곤 하였다. 그러나, 불필요하게 PC 저장공간을 차지하기도 하고 
팀 프로젝트나 오픈소스에 기여할려면 Github 공부가 반드시 필요하다고 느껴졌다. 기본적인 사용법은 다음과 같다. 브랜치는 기본으로 설정되어 있는 &#39;main&#39;을 사용했다.</p>
<h3 id="리포지토리를-처음-만들-경우">리포지토리를 처음 만들 경우</h3>
<h4 id="1-로컬-리포지토리를-만들-폴더에-터미널로-접속해서-다음을-입력한다">1. 로컬 리포지토리를 만들 폴더에 터미널로 접속해서 다음을 입력한다.**</h4>
<pre><code>  &gt;  git init</code></pre><p><strong>2. 깃헙 프로필에서 1번의 폴더와 연동할 리포지토리를 만들고 (만들 시 기본적인 .gitignore, README.md도 자동생성되게 한다) 주소를 복사한다.</strong>
</br>
<strong>3. 2번과 연동하기 위하여 터미널에 다음을 입력한다.</strong></p>
<blockquote>
<p>git remote add origin &lt;2번에서 복사한 깃헙 프로필 리포지토리 주소&gt;
텍스트</p>
</blockquote>
<p>*<em>4. 2번에서 만든 파일에 대한 정보를(변경사항) 받아온다. *</em></p>
<blockquote>
<p>git fetch origin</p>
</blockquote>
<p><strong>5.    2번에서 발생한 변경사항을 로컬 리포지토리에 입력 및 동기화한다.</strong></p>
<blockquote>
<p>git merge origin/main</p>
</blockquote>
<p>보통 fetch 및 merge 대신 pull를 많이 사용하지만 pull를 사용할 시 오류가 너무 자주 발생하였다. 대안책으로 merge를 사용하면 오류가 없다. 위의 merge대신 rebase를 해도된다. &#39;merge&#39;대신 &#39;rebase&#39;를 입력하면 된다. 깃헙의 기본 작동방식인 merge에 익숙하기에 merge를 택했다.</p>
<p>만약 로컬 리포지토리에 변경사항(파일 수정, 추가, 삭제 등)이 전혀 없었다면 이대로 끝내면 된다. 하지만 난 2번에서 만든 파일들 외에도 로컬 리포지토리에서 파일수정을 하였기에 아래의 단계가 불가피했다. *<em>로컬 리포지토리에서의 수정사항을 리모트 저장소 (편의 상 리포지토리를 리포 및 저장소라고 하겠다)에 푸쉬 즉, 동기화해야 하기 때문이다. *</em></p>
<p>** 6. 로컬 저장소의 수정 된 코드를 리모트 저장소에 푸쉬.**</p>
<blockquote>
<p>git add .
git commit -m &quot;your commit message here&quot; (변경사항 내용)
git push origin main </p>
</blockquote>
<p>로컬과 리모트 저장소의 동기화가 요류없이 완료되었다. <span style="color:indianred"><strong>위의 단계를 모두 완료한 이후 추후 로컬 저장소만 수정을 하고 리모트는 수정된 사항이 없을 때 6번만 로컬 저장소에서 시행하면 끝이다.</strong></span>
이 경우가 아닌, 리모트와 로컬 저장소를 둘 다 수정하게 될 시 다음을 실행한다.
<br><br/></p>
<hr>
<h3 id="생성-및-동기화-되어있는-로컬리모트-저장소를-수정했을-경우">생성 및 동기화 되어있는 로컬/리모트 저장소를 수정했을 경우</h3>
<p>위의 4번을 실행한 후 다음을 터미널에 입력한다. </p>
<blockquote>
<p>git stash</p>
</blockquote>
<p>&#39;stash&#39; 없이 git add와 commit및 merge를 하게 되면 5번을 실행 시 merge오류가 발생한다. git은 리모트와 로컬 저장소의 코드가 동일하다는 전제하에 작동하기 때문이다. stash는 로컬 저장소의 변경사항을 바로 적용하지 않고 보류하여 따로 저장해둔다. 위의 명령어를 입력하면 git status 에 출력되어 있었던 변경사항이 사라진다. stash로 임시저장 하였기 때문이다. </p>
</br>
그 다음, 위의 5번을 입력한다. 5번을 입력한 다음, 다음을 입력한다. stash에 따로 저장했던 변경사항을 다시 가져오는 명령어다. 변경사항이 적용된다. git status를 입력하면 변경사항이 다시 보이는 걸 확인할 수 있다.

<blockquote>
<p>git stash pop</p>
</blockquote>
<p>다음으로, 위의 git add 부터 git push 코드까지 입력한다. 리모트 저장소의 수정내용은 로컬에 머지가 되고 로컬의 수정내용은 리모트 저장소에 푸쉬가 완료된다. 
<span style="color:red"><strong>앞서 말했듯이 로컬과 리모트 저장소의 커밋 진행현황이 같아야 리모트 저장소로 푸쉬가 가능하니 반드시 리모트 저장소의 변경내용을 우선적으로 머지한 뒤 푸쉬를 해야한다.</strong></span> 다음의 명령어를 터미널에 입력하면 커밋 진행 현황을 알 수 있다. 두 저장소의 커밋현황을 비교해보면 두 저장소가 동기화가 잘 되고 있는지 알 수 있다. </p>
<h4 id="로컬-저장소-커밋-진행현황">로컬 저장소 커밋 진행현황</h4>
<blockquote>
<p>git log --oneline</p>
</blockquote>
<h4 id="리모트-저장소-커밋-진행현황">리모트 저장소 커밋 진행현황</h4>
<blockquote>
<p>git log --oneline origin/main</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] Errors from 'npm install...'.]]></title>
            <link>https://velog.io/@infinity-blue/React-Errors-from-npm-install</link>
            <guid>https://velog.io/@infinity-blue/React-Errors-from-npm-install</guid>
            <pubDate>Wed, 06 Sep 2023 12:19:00 GMT</pubDate>
            <description><![CDATA[<h2 id="background--problem">Background / Problem</h2>
<p>Package를 설치하다보면 반드시 설치 중 다양한 오류를 마주치게 된다. 예를 들어,
&#39;agoliasearch,&#39; &#39;firebase-admin&#39;을 설치하는 도중에 기존에 있던 패키지와의 충돌 및 버전 문제로 설치를 더 이상 진행할 수 없다는 오류 몇 가지를 겪게 된다:</p>
<h4 id="오류-1-">*<em>오류 1 *</em></h4>
<p>&#39;Cannot resolve &lt;&#39;package name&#39;&gt;</p>
<p><strong>오류 2(대표적인 오류)</strong></p>
<p>다수의 패키지 호환 오류와 함께 &#39;--legacy-peer-deps&#39; 및 &#39;npm audit fix&#39;를 권장.
이 두 가지 장애를 연속으로 마주쳤고 아래와 같은 시도를 했었다.</p>
<p><br><br></p>
<h2 id="solution">Solution</h2>
<h4 id="1-콘솔에-나온대로-시행한다-예를-들어-콘솔에서---legacy-peer-deps을-권장할-시">1. 콘솔에 나온대로 시행한다. 예를 들어, 콘솔에서 --legacy-peer-deps을 권장할 시,</h4>
<blockquote>
<p>npm install &lt;&#39;package name&#39;&gt; --legacy-peer-deps</p>
</blockquote>
<p>이외에 만약, &#39;npm audit fix&#39;를 실행해보라고 권장한다면 그대로 입력한다. 그래도 해결이 안될 시 다음을 시행한다. <br><br></p>
<h4 id="2-clear-the-npm-cache-해결이-안될-시-3번-시행">2. Clear the npm cache (해결이 안될 시 3번 시행):</h4>
<blockquote>
<pre><code>   npm cache clean --force</code></pre></blockquote>
<h4 id="3-delete-node_modules-and-package-lockjson">3. Delete node_modules and package-lock.json:</h4>
<blockquote>
</blockquote>
<p>rm -rf node_modules package-lock.json</p>
<h4 id="4-install-the-dependencies-u3번-이후-반드시-실행u">4. Install the dependencies (<u>3번 이후 반드시 실행</u>):</h4>
<blockquote>
<p>npm install</p>
</blockquote>
<h4 id="5-수동으로-직접-패키지-설치-위-방법으로-해결이-안될-시-시행">5. 수동으로 직접 패키지 설치 (위 방법으로 해결이 안될 시 시행)</h4>
<blockquote>
<p>package.json에 리스트 된 패키지 버전을 확인하며 그 버전에 맞는 패키지를 직접 수동으로 설치한다. (<strong>e.g. npm install &lt;&#39;package name&#39;&gt;@&lt;&#39;version&#39;&gt;</strong> </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Expo] ERROR in ./node_modules/dotenv/..]]></title>
            <link>https://velog.io/@infinity-blue/Expo-ERROR-in-.nodemodulesdotenv</link>
            <guid>https://velog.io/@infinity-blue/Expo-ERROR-in-.nodemodulesdotenv</guid>
            <pubDate>Sat, 22 Jul 2023 16:55:45 GMT</pubDate>
            <description><![CDATA[<h2 id="problem">PROBLEM:</h2>
<p>Expo 프로젝트를 퍼블리쉬 하기 위해서 서버 주소를 로컬에서 pythonanywhere 서버로 이전하려고 한다. </p>
<h2 id="attempts">ATTEMPTS:</h2>
<p>react-native-dotenv 패키지를 설치하여 dotenv를 사용하려 하였더니 아래와 같은 에러가 뜬다. <br><br/></p>
<img src="https://velog.velcdn.com/images/infinity-blue/post/9c052aaa-73d8-4e66-ac2c-60e9c454d580/image.png" width= "70%" height= "70%">

<br>

<h2 id="solution">SOLUTION:</h2>
<p>dotenv를 사용하려면 Node.js에 내재된 모듈이 필요한데 Expo에는 내재되어 있지 않기 때문에 에러가 뜬다. React Native의 전역변수인  _ _DEV _ _를 아래와 같이 사용하면 해결된다.<br><br/></p>
<p>*<em>1. 프로젝트의 최상위 폴더(Root directory)에 config.js 파일을 만든다. *</em>
config.js</p>
<pre><code class="language-javascript">// A server url will be allocated depending on whether you are in dev or production mode.
const SERVER_URL = __DEV__ ? &#39;http://localhost:8000/&#39; : &#39;https://&lt;your-username&gt;.pythonanywhere.com/&#39;;
export { SERVER_URL };</code></pre>
<br>

<p>** 2. 로컬서버 주소를 아래와 같이 대체한다. 예를 들어 로컬서버 주소가 <a href="http://localhost:8000/buy">http://localhost:8000/buy</a> 라고 가정하면 이를 SERVER_URL + &#39;buy&#39;로 대체하면 된다.**</p>
<pre><code class="language-javascript">import { SERVER_URL } from &#39;./config&#39;;

// Replace Local server address as below.
fetch(SERVER_URL + &#39;endpoint&#39;);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Django] Cookie  다루기]]></title>
            <link>https://velog.io/@infinity-blue/Django-Cookie-%EB%8B%A4%EB%A3%A8%EA%B8%B0</link>
            <guid>https://velog.io/@infinity-blue/Django-Cookie-%EB%8B%A4%EB%A3%A8%EA%B8%B0</guid>
            <pubDate>Mon, 17 Jul 2023 17:56:13 GMT</pubDate>
            <description><![CDATA[<p>Django에서 cookie를 설정하는 방법에 앞서 Session과 Cookie의 차이점을 알아보자.</p>
<h2 id="cookie">Cookie</h2>
<blockquote>
<p>브라우저가 서버로부터 받은 쿠키(사용자 정보)를 사용자의 웹 부라우저에 저장함. 웹 브라우저가 종료되면 삭제되지만, 만료 날짜를 정해둘 시 만료일에 삭제된다.</p>
</blockquote>
<h2 id="session">Session</h2>
<blockquote>
<p>서버로부터 응답받은 세션 ID가 담긴 쿠키를 브라우저에 저장하고 이 쿠키를 통해 웹 서버간의 커뮤니케이션을 실행함(세션 ID는 서버에 저장된 사용자의 정보에 접근하기 위한 도구). 웹 브라우저가 종료되면 세션 쿠키는 삭제된다.</p>
</blockquote>
<br/>



<h3 id="쿠키-생성-및-쿠키-유무-확인">쿠키 생성 및 쿠키 유무 확인</h3>
<h4 id="viewspy">views.py</h4>
<pre><code>def checkCookie(request):
    mycookie_value = request.COOKIES.get(&#39;mycookie&#39;)
    if mycookie_value == &#39;cookievalue&#39;:
        # Cookie exists and has the expected value
    else:
        # Cookie is not set or has a different value

</code></pre><pre><code>def set_cookie(request):
    response = HttpResponse(&quot;Cookie Set!&quot;)
    response.set_cookie(&#39;mycookie&#39;, &#39;cookie_value&#39;)
    return response
</code></pre><br/>

<h3 id="쿠키-삭제">쿠키 삭제</h3>
<h4 id="viewspy-1">views.py</h4>
<pre><code>def deleteCookie(request):
    response = HttpResponse(&quot;Cookie Deleted!&quot;)
    response.delete_cookie(&#39;mycookie&#39;)
    return response</code></pre><p>위의 함수를 실행시키면 서버에 있는 쿠키가 삭제되는데, <u>*<em>사용자의 부라우저에 있는 쿠키도 아래와 같이 삭제를 해야한다. *</em></u></p>
<h4 id="client-side">client-side</h4>
<pre><code>  function clearCookie(name) {
    document.cookie = name + &#39;=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;&#39;;
  }</code></pre><p><strong>Reference</strong>
<a href="https://www.pythontutorial.net/django-tutorial/django-cookies/">https://www.pythontutorial.net/django-tutorial/django-cookies/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Pythonanywhere] This  virtualenv seems to have the wrong Python version.]]></title>
            <link>https://velog.io/@infinity-blue/Pythonanywhere-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@infinity-blue/Pythonanywhere-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Tue, 04 Jul 2023 17:30:22 GMT</pubDate>
            <description><![CDATA[<p>아래링크를 따라 똑같이 실행했는데 서버를 접속할 시 로컬에서는 뜨지 않았던 <strong>Refused to connect</strong> 오류가 뜬다.<br><a href="https://velog.io/@hyereen/djangodeploypythonanywhere">https://velog.io/@hyereen/djangodeploypythonanywhere</a></p>
<img src="https://velog.velcdn.com/images/infinity-blue/post/263d0790-0372-48b5-a6c5-2591bd464233/image.png" width="60%" height="60">

<img src="https://velog.velcdn.com/images/infinity-blue/post/91aac1bb-723f-4147-81f3-73c6020ee191/image.png" width="60%" height="60">
<br>

<h3 id="attempts">Attempts:</h3>
<p>파이썬을 재설치 및 장고 프로젝트의 settings.py에서 CORS_ALLOWED_ORIGINS 및 ALLOWED_HOSTS 에 해당 서버주소를 넣었지만 문제는 지속되었다.
<br></p>
<h3 id="solution">Solution:</h3>
<p><a href="https://help.pythonanywhere.com/pages/Virtualenvs">Pythonanywhere 공식사이트</a>
<a href="https://blog.devgenius.io/django-tutorial-deploy-a-django-app-to-pythonanywhere-for-free-269ec4e7b263">https://blog.devgenius.io/django-tutorial-deploy-a-django-app-to-pythonanywhere-for-free-269ec4e7b263</a>)<p/></p>
<hr>
<h3 id="things-to-remember">Things to remember:</h3>
<ul>
<li>프로젝트 폴더는 ** <span style="color:indianred">/home/&lt;&quot;username&quot;&gt;/ ** 경로에 저장한다. (wsgi.py 참고)</li>
<li>가상환경은 /<strong><span style="color:indianred">home/&lt;&quot;username&quot;&gt;/.virtualenv/</strong> 경로에 저장한다. </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Error] Ohmyzsh prompt is slow on vscode.]]></title>
            <link>https://velog.io/@infinity-blue/Error-Ohmyzsh-prompt-is-slow-on-vscode</link>
            <guid>https://velog.io/@infinity-blue/Error-Ohmyzsh-prompt-is-slow-on-vscode</guid>
            <pubDate>Fri, 30 Jun 2023 12:17:56 GMT</pubDate>
            <description><![CDATA[<h2 id="problem">Problem:</h2>
<p>bash에서 zsh로 전환한 후 성공적으로 ohmyzsh를 설치하였다. 몇일 후 vscode에서 업데이트 여부를 묻길래 omz update를 실행하였다. 그 이후로  vscode에서 terminal 를 사용 시 반응속도가 현저히 느렸다. (로컬에 설치된 Iterm은 속도에 문제가 없다.) </p>
<h2 id="attempts">Attempts:</h2>
<p>ohmyzsh 재설치를 하였지만 개선이 없어 구글검색으로 얻은 몇가지 해결책을 실행했다.</p>
<h2 id="solution">Solution:</h2>
<h4 id="1-터미널-로그-삭제">1. 터미널 로그 삭제</h4>
<pre><code>sudo rm -rf /private/var/log/asl/*.asl</code></pre><h4 id="2-vscode의-settingsjson에-아래의-코드-삽입">2. vscode의 settings.json에 아래의 코드 삽입.</h4>
<pre><code>  &quot;terminal.integrated.allowChords&quot;: false,
  &quot;terminal.integrated.drawBoldTextInBrightColors&quot;: false,
  &quot;terminal.integrated.experimentalLinkProvider&quot;: false,
  &quot;terminal.integrated.rendererType&quot;: &quot;auto&quot;</code></pre><p>아래와 같이 실행한 후 vscode 프로그램 창만 닫았다가 다시 열었을 땐 문제가 해결되지 않았다.<br><span style="color: red">하지만, vscode를 완전히 종료(quit)한 후 Terminal 창을 새로 열었을 땐 속도가 대단히 빨라진 것을 체감하였다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'getUserMedia')]]></title>
            <link>https://velog.io/@infinity-blue/Uncaught-in-promise-TypeError-Cannot-read-properties-of-undefined-reading-getUserMedia</link>
            <guid>https://velog.io/@infinity-blue/Uncaught-in-promise-TypeError-Cannot-read-properties-of-undefined-reading-getUserMedia</guid>
            <pubDate>Wed, 03 May 2023 10:55:30 GMT</pubDate>
            <description><![CDATA[<h2 id="problem"><strong>Problem:</strong></h2>
<pre><code class="language-javascript">useEffect(() =&gt; {
        const getPopWindow = async () =&gt; {
            if (!popup) {
                try {
                    await navigator.mediaDevices.getUserMedia({ video: true }).then(
                        function success(stream) {

                        })
                        .catch(function (error) {
                            console.log(error);
                        }
                        )
                } catch (error) { console.log(error) }
            }
        }
        getPopWindow()
    }, [popup])
</code></pre>
<p>위와 같은 코드를 실행하였을 때 제목과 같은 에러 메시지가 뜬다.
<img src="https://velog.velcdn.com/images/infinity-blue/post/c24e19b9-f83e-4b24-b6ff-0207d6b36e3d/image.png" width="50%" height="50%"></p>
<h2 id="attempts">Attempts:</h2>
<p>내가 시도해 본 것은 다음과 같지만 오류는 그대로였다.</p>
<ul>
<li>chrome://flags -&gt; Insecure origins treated as secure 란에 해당 웹 주소 입력하기.</li>
<li>https로 웹 접속.</li>
<li>브라우저 캐시 삭제</li>
</ul>
<h2 id="solution"><strong>Solution:</strong></h2>
<pre><code class="language-javascript">await navigator.mediaDevices.getUserMedia({ video: true })...</code></pre>
<p>위 코드블락의 <strong>mediaDevices</strong> 부분을 삭제하고 다시 썼더니 오류가 해결되었다. 아마 코드 에디터 내의 캐시 문제가 아니였을까 싶다.</p>
]]></description>
        </item>
    </channel>
</rss>