<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>아이테킨의 끄적끄적</title>
    <link>https://aytekin.tistory.com/</link>
    <description>데이터 분석을 공부하고 있습니다.
이외에도 다양한 분야에 대해서 자유롭게 이야기하고 나누길 원합니다.</description>
    <language>ko</language>
    <pubDate>Sun, 12 Jul 2026 04:06:27 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Aytekin</managingEditor>
    <image>
      <title>아이테킨의 끄적끄적</title>
      <url>https://tistory1.daumcdn.net/tistory/4708314/attach/7454247c5d4d49d08d28ae9d8ba52bff</url>
      <link>https://aytekin.tistory.com</link>
    </image>
    <item>
      <title>다이랙트 결혼준비 웨딩카페 포인트 댓글, 좋아요 자동 프로그램 개발기 | 파이썬 tkinter .exe파일 | chatGPT api 활용</title>
      <link>https://aytekin.tistory.com/149</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 준비할 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 이 프로그램을 돌리려면 openai api key가 필요하다.&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;chatGPT의 모델을 chatgpt페이지가 아닌 나의 프로그램과 연결시켜 사용하려면, api라는것을 이용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무료로 api를 공개하는 서비스도 많지만 chatGPT는 유료로만 이용할 수 있다.&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;a href=&quot;https://openai.com/ko-KR/api/&quot;&gt;https://openai.com/ko-KR/api/&lt;/a&gt; &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 페이지에 가서 계정 만들고, 돈을 충전하고 api키를 받아놓아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최소 5$은 충정해야 하는것 같다.&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;939&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkbYTJ/dJMb99Lp8Q8/p8EKZo4W8O6kxHKc3r1Xu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkbYTJ/dJMb99Lp8Q8/p8EKZo4W8O6kxHKc3r1Xu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkbYTJ/dJMb99Lp8Q8/p8EKZo4W8O6kxHKc3r1Xu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkbYTJ%2FdJMb99Lp8Q8%2Fp8EKZo4W8O6kxHKc3r1Xu1%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;939&quot; height=&quot;438&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;438&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 api키를 발급하는데 성공했다면 우선 준비해야할 것은 다 마친 것이다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 사용 라이브러리 및 개발 환경&lt;/h2&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&gt;&lt;b&gt;python 3.11.2&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;openai&lt;/b&gt; =&amp;gt; openai api를 사용하기 위한 라이브러리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;selenium&lt;/b&gt; =&amp;gt; 네이버 계정 로그인 및 크롬 브라우저 제어를 위한 필수라이브러리다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;pyperclip&lt;/b&gt; =&amp;gt; 네이버 로그인 시 캡차를 피하기 위해 사용한다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;pyinstaller&lt;/b&gt; =&amp;gt; 개발 완료 후 exe파일로 내려받아 편리하게 사용할 때&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;tkinter&lt;/b&gt; =&amp;gt; GUI인터페이스 개발을 위한 파이썬 라이브러리다. 비록 UI는 구식이지만 편리하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 라이브러리 설치 리스트는 깃헙의 requirements.txt를 참고하시길..&lt;/p&gt;
&lt;pre id=&quot;code_1761637778420&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install -r requirements.txt  #다들 아시죠?ㅎㅎ&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;&lt;b&gt;OS : 윈도우(Window)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 Mac이랑은 친하지가 않아서... 윈도우에서만 해봤고 맥은 안해봤다. 아마도 안될거다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 프로젝트 개요&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 프로젝트 디랙토리 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 3개의 파이썬 파일로 이 프로젝트를 구성하였다.&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;main.py&lt;br /&gt;&lt;/b&gt;tkinter 앱 관련 내용을 선언하고, 네이버 카페 관련 기능들을 처리해줄 NaverCafeBot이라는 클래스 2가지로 이루어져 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;helpers.py&lt;br /&gt;&lt;/b&gt;글자수를 세고, 텍스트 전처리하는 등의 util용 함수를 정의하였으며, 댓글 생성 시 사용할 프롬프트와 함수를 정의하였다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;config.py&lt;br /&gt;&lt;/b&gt;설정값들을 정의하는 부분이다. 네이버 아이디, LLM설정값, 댓글 달 개수, 최대 페이지 개수, 좋아요 할지 말지 등등 여러가지 설정값들은 다 여기에 모았다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 주요 기능&lt;/h3&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&gt;&lt;b&gt;자동 로그인&lt;/b&gt;&lt;br /&gt;네이버 ID / 비밀번호를 받아 selenium/ &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;pyperclip&lt;span&gt; 을 이용한 자동 로그인&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;b&gt;커뮤니티 별 최적화된 프롬프트 사용&lt;/b&gt;&lt;br /&gt;웨딩카페 내에 여러가지 커뮤니티/ 리뷰 파트로 구분되어 있다. 각 게시글에 맞게 LLM이 댓글을 쓰는 것을 돕기위해서 프롬프트를 커뮤니티/ 리뷰 파트별로 최적화하였다&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;LLM 기반 댓글 생성 및 자동 등록&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;현재 게시글의 제목+내용을 읽고 적절한 댓글을 작성하고 등록할 수 있다.&lt;br /&gt;gpt모델(gpt-4o-mini, gpt-4.1-mini등등..), 댓글 톤(따뜻, 담백, 친근, 유머러스), 댓글길이(길게, 중간, 짧게)을 사용자가 선택하여 사용할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;b&gt;자동 좋아요&lt;/b&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;해당 게시글에 자동 좋아요를 수행할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;b&gt;(로컬환경 한정) 개인정보 저장&lt;/b&gt;&lt;br /&gt;민감한 개인정보(아이디, 패스워드, api키 등)을 하드코딩하는건 매우 위험한 방식이다. 최소한의 보안로직이 필요한데 내가 사용한 방법은 로컬환경에 파일을 하나 쓰고 거기에 개인정보를 저장하고 읽어오는 방식이다.&lt;br /&gt;프로그램을 실행할 때 마다 개인정보를 쓰는것도 귀찮기 때문에...&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;br /&gt;(나중엔 클라우드 서버에서 매일 1번씩 자동으로 돌도록 해봐야겠다.)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;b&gt;로그 표시 및 로그 레벨 필터링&lt;/b&gt;&lt;br /&gt;프로그램이 돌아가면서 찍어내는 로그들을 볼 수 있도록 창을 만들었고, 로그 레벨을 선택할 수도 있도록 하였다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3 플로우(Flow)&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;635&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A7MZC/dJMcaf5Wl1C/pdM70zI3OQPU6y0p7SJ4h0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A7MZC/dJMcaf5Wl1C/pdM70zI3OQPU6y0p7SJ4h0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A7MZC/dJMcaf5Wl1C/pdM70zI3OQPU6y0p7SJ4h0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA7MZC%2FdJMcaf5Wl1C%2FpdM70zI3OQPU6y0p7SJ4h0%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;1828&quot; height=&quot;635&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;635&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;/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;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 소회&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동화에 대한 생각이 많아지는 요즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시대는 하루하루 빠르게 흘러가고 변해가고 있음을 많이 느낀다.&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;/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;/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;그래서 나는 하루에 10개만 돌릴 생각이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(어짜피 api비용이 더 나올거 같다... 생각보다 openai비용이 비싸다)&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;시간은 좀 오래걸린거 같고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 정리하는데 사실 더 오랜시간이 걸린거 같은데&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;h2 data-ke-size=&quot;size26&quot;&gt;5. 코드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(관련 코드 및 깃헙 링크)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/aytekin827/dw_comment_automation&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/aytekin827/dw_comment_automation&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>파이썬 python/자동화</category>
      <category>GPT</category>
      <category>Tkinter</category>
      <category>결혼준비</category>
      <category>다이랙트</category>
      <category>댓글</category>
      <category>웨딩카페</category>
      <category>자동화</category>
      <category>좋아요</category>
      <category>파이썬</category>
      <category>포인트</category>
      <author>Aytekin</author>
      <guid isPermaLink="true">https://aytekin.tistory.com/149</guid>
      <comments>https://aytekin.tistory.com/149#entry149comment</comments>
      <pubDate>Tue, 28 Oct 2025 17:06:24 +0900</pubDate>
    </item>
    <item>
      <title>AWS RDS MySQL Dbeaver로 로컬에서 연결(퍼블릭엑세스 사용 없이) - ec2 ssh 터널링 이용</title>
      <link>https://aytekin.tistory.com/148</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 퍼블릭엑세스(Public Access)를 허용하지 않는 이유&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*&lt;b&gt;과금, 비용, 돈&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 프리티어를 이용해서 몇가지 테스트를 해보고 있었는데, RDS에 퍼블릭 엑세스를 허용하였더니&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;과금이 된 이유는 VPC때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VPC는 AWS에서 서비스, 솔루션을 제공할 때 필요한 네트워크 환경인데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼블릭엑세스를 허용하게 되면 이 네트워크를 공개적으로 사용할 수 있도록 열어놓는 것이기에 과금이 된다고 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래는 과금이 안됬었는데 2024년부터 바뀌었다고 한다.&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;b&gt;보안적 위험성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼블릭엑세스 허용 시 RDS가 퍼블릭 IP를 가지게 되어 전세계의 누구라도 접속을 시도할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 데이터베이스의 id, password도 있고, 보안그룹을 설정할 수 있어서 DB접속을 막을 순 있지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;접속시도&quot;자체는 막을수가 없다.&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;무차별 대입공격(Brute force attacks), SQL인젝션 등 자동화된 공격에 노출될 위험이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 개발자의 실수로 보안정보를 하드코딩하여 github이나 다른곳에 노출이 된다면 너무 위험한 상황이 된다.&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;b&gt;2. 로컬 접속은 어떻게? ➡️ EC2 ssh 터널링&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 개발할 때 로컬환경에서 DB에 접속하지 못한다면 여간 불편한 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 로컬환경에서 rds에 접근할 수 있는 방법은 무엇이냐!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 EC2를 이용한 SSH 터널링이라는 기술이다.&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 인스턴스를 만들어서 접속할 때 보통은 pem ,ppk키값을 가지고 접속해본적이 있을것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그때 사용하는 프로토콜과 포트가 ssh 22번 포트이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 ssh연결을 터널처럼 통과해서 rds에 접근한다는 의미이다.&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가 외부에 노출된 IP를 가지고 있는건 마찬가지 이지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그pem, ppk 키 값을 가지고 있어야만 데이터베이스에 접근할 수 있어서 보안적으로 더 안전하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;983&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nQ5Dl/btsOK7Cjt3Q/xcRyxoLKcVW3Ri1VLyzdk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nQ5Dl/btsOK7Cjt3Q/xcRyxoLKcVW3Ri1VLyzdk1/img.png&quot; data-alt=&quot;내가 이해한 바를 도식화해보았다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nQ5Dl/btsOK7Cjt3Q/xcRyxoLKcVW3Ri1VLyzdk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnQ5Dl%2FbtsOK7Cjt3Q%2FxcRyxoLKcVW3Ri1VLyzdk1%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;1920&quot; height=&quot;983&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;983&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;내가 이해한 바를 도식화해보았다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 도식처럼 EC2를 타고 RDS에 접근하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2는 client로부터 오는 요청받고 그 요청을 다시 RDS로 보내주는 것(Port Forwarding)이다.&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;b&gt;3. dbeaver로 ec2 터널링 이용해서 rds연결하기&lt;/b&gt;&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;/p&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-sourcepos=&quot;8:5-8:56&quot;&gt;Public Access가 비활성화된 RDS 인스턴스 (Private Subnet에 위치)&lt;/li&gt;
&lt;li data-sourcepos=&quot;8:5-8:56&quot;&gt;RDS와 동일한 VPC 내의 Public Subnet에 위치한 EC2 인스턴스 (Bastion Host 역할)&lt;/li&gt;
&lt;li data-sourcepos=&quot;8:5-8:56&quot;&gt;EC2 인스턴스에 접속할 수 있는 SSH Key Pair (.pem 파일)&lt;/li&gt;
&lt;li data-sourcepos=&quot;8:5-8:56&quot;&gt;로컬 DB 클라이언트 - DBeaver (MySQL Workbench, DataGrip 등 다른 클라이언트 프로그램도 가능할것이다.)&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;442&quot; data-origin-height=&quot;385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZOZSy/btsOKCCZzUK/NtowKvRgAg0Zh5oeTjsog1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZOZSy/btsOKCCZzUK/NtowKvRgAg0Zh5oeTjsog1/img.png&quot; data-alt=&quot;퍼블릭엑세스 불가능 체크해주자.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZOZSy/btsOKCCZzUK/NtowKvRgAg0Zh5oeTjsog1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZOZSy%2FbtsOKCCZzUK%2FNtowKvRgAg0Zh5oeTjsog1%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;442&quot; height=&quot;385&quot; data-origin-width=&quot;442&quot; data-origin-height=&quot;385&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;퍼블릭엑세스 불가능 체크해주자.&lt;/figcaption&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;b&gt;Client =&amp;gt; EC2(Bastion Host) =&amp;gt; RDS&lt;/b&gt; 이 순서로 요청이 흘러갈테니&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDS부터 거꾸로 설정을 진행해보자.&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;&lt;b&gt;RDS 보안설정&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;RDS 인스턴스의 보안 그룹(security group) 규칙에 EC2(Bastion Host)의 프라이빗 IP 주소 또는 보안그룹 ID에서 RDS 포트(MySQL 3306)을 허용한다.&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;731&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rsftK/btsOKpw7g8X/oOZiz3k65j7XYL8sc8p2tK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rsftK/btsOKpw7g8X/oOZiz3k65j7XYL8sc8p2tK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rsftK/btsOKpw7g8X/oOZiz3k65j7XYL8sc8p2tK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrsftK%2FbtsOKpw7g8X%2FoOZiz3k65j7XYL8sc8p2tK%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;731&quot; height=&quot;395&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;395&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;1023&quot; data-origin-height=&quot;429&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4dJhM/btsOLAqNsrc/Q3YbJqnxRTUs2OtbzrCPB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4dJhM/btsOLAqNsrc/Q3YbJqnxRTUs2OtbzrCPB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4dJhM/btsOLAqNsrc/Q3YbJqnxRTUs2OtbzrCPB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4dJhM%2FbtsOLAqNsrc%2FQ3YbJqnxRTUs2OtbzrCPB1%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;1023&quot; height=&quot;429&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;429&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;925&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xXpr7/btsOJGMUvpp/JTBoYXjKSKML3nV5kBhnHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xXpr7/btsOJGMUvpp/JTBoYXjKSKML3nV5kBhnHk/img.png&quot; data-alt=&quot;bastion host 서버의 프라이빗 IP주소를 복사&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xXpr7/btsOJGMUvpp/JTBoYXjKSKML3nV5kBhnHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxXpr7%2FbtsOJGMUvpp%2FJTBoYXjKSKML3nV5kBhnHk%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;925&quot; height=&quot;488&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;bastion host 서버의 프라이빗 IP주소를 복사&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kG56j/btsOKlIidqL/EecYVFxmme2zUGLiXggZF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kG56j/btsOKlIidqL/EecYVFxmme2zUGLiXggZF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kG56j/btsOKlIidqL/EecYVFxmme2zUGLiXggZF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkG56j%2FbtsOKlIidqL%2FEecYVFxmme2zUGLiXggZF0%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;1104&quot; height=&quot;324&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;324&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;b&gt;Bastion Host(EC2) 보안설정&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;Public Subnet에 EC2를 생성하고, SSH접속을 허용하도록 보안그룹을 세팅한다.&lt;/li&gt;
&lt;li&gt;이 보안그룹의 인바운드 규칙에는 본인 로컬 IP 주소에서 SSH 포트(22)로의 접근만 허용하도록 설정한다.&lt;/li&gt;
&lt;li&gt;(옵션) 탄력적 IP(Elastic IP)를 설정해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 탄력적 IP는 개념상 필수는 아닌 것 같지만, 나의 경우엔 탄력적 IP를 연결해주었을 때 잘 연결되었다. 아무래도 EC2의 IP가 언제 변할 지 모르니 해두는게 좋겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1133&quot; data-origin-height=&quot;219&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFOoro/btsOL5D3Q0m/prlXe1ait4FfLT0WqMddQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFOoro/btsOL5D3Q0m/prlXe1ait4FfLT0WqMddQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFOoro/btsOL5D3Q0m/prlXe1ait4FfLT0WqMddQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFOoro%2FbtsOL5D3Q0m%2FprlXe1ait4FfLT0WqMddQk%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;1133&quot; height=&quot;219&quot; data-origin-width=&quot;1133&quot; data-origin-height=&quot;219&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그림처럼 내 IP 를 누르면 현재 아이피를 찾아준다.&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;&lt;b&gt;SSH 터널링 설정(로컬 DB 클라이언트 - DBeaver)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[ssh 탭]&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;SSH 호스트 : Bastion Host 의 퍼블릭 IP 주소 또는 퍼블릭 DNS&lt;/li&gt;
&lt;li&gt;SSH 사용자 이름 : EC2 인스턴스의 기본 사용자 (예: &quot;ec2-user&quot; for Amazon Linux, &quot;ubuntu&quot; for Ubuntu, &quot;admin&quot; for Windows)&lt;/li&gt;
&lt;li data-sourcepos=&quot;23:9-23:55&quot;&gt;SSH 키 파일: Bastion Host 생성 시 사용한 .pem 파일&lt;/li&gt;
&lt;li data-sourcepos=&quot;23:9-23:55&quot;&gt;advanced settings의 implementation SSHJ 선택&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;931&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kMGsM/btsOJHLTeP0/9BOPFPERWrSoXzUlqKtKkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kMGsM/btsOJHLTeP0/9BOPFPERWrSoXzUlqKtKkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kMGsM/btsOJHLTeP0/9BOPFPERWrSoXzUlqKtKkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkMGsM%2FbtsOJHLTeP0%2F9BOPFPERWrSoXzUlqKtKkK%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;931&quot; height=&quot;495&quot; data-origin-width=&quot;931&quot; data-origin-height=&quot;495&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;511&quot; data-origin-height=&quot;157&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lfos6/btsOKIiL532/HGVmToz2IEpV5a5Gn0bcPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lfos6/btsOKIiL532/HGVmToz2IEpV5a5Gn0bcPk/img.png&quot; data-alt=&quot;Test tunnel configuration 이 잘 되는지 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lfos6/btsOKIiL532/HGVmToz2IEpV5a5Gn0bcPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flfos6%2FbtsOKIiL532%2FHGVmToz2IEpV5a5Gn0bcPk%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;157&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;157&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Test tunnel configuration 이 잘 되는지 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;br /&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;size16&quot;&gt;&lt;b&gt;[Main 탭]&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-sourcepos=&quot;25:9-25:31&quot;&gt;server host: RDS 엔드포인트&lt;/li&gt;
&lt;li data-sourcepos=&quot;26:9-26:37&quot;&gt;port : RDS 포트 (예: 3306)&lt;/li&gt;
&lt;li data-sourcepos=&quot;26:9-26:37&quot;&gt;username : RDS생성시 설정한 username&lt;/li&gt;
&lt;li data-sourcepos=&quot;26:9-26:37&quot;&gt;Password : RDS생성시 설정한 password&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;680&quot; data-origin-height=&quot;530&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6IVVp/btsOLF6Sdmo/PdUTtb31nMZEzPp3GqF4c0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6IVVp/btsOLF6Sdmo/PdUTtb31nMZEzPp3GqF4c0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6IVVp/btsOLF6Sdmo/PdUTtb31nMZEzPp3GqF4c0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6IVVp%2FbtsOLF6Sdmo%2FPdUTtb31nMZEzPp3GqF4c0%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;680&quot; height=&quot;530&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;530&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;680&quot; data-origin-height=&quot;537&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/weyCp/btsOKA6vzGg/vFLZtsHLAmQ4WenX376ZLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/weyCp/btsOKA6vzGg/vFLZtsHLAmQ4WenX376ZLK/img.png&quot; data-alt=&quot;test connection 연결 확인하자.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/weyCp/btsOKA6vzGg/vFLZtsHLAmQ4WenX376ZLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FweyCp%2FbtsOKA6vzGg%2FvFLZtsHLAmQ4WenX376ZLK%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;680&quot; height=&quot;537&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;537&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;test connection 연결 확인하자.&lt;/figcaption&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 data-sourcepos=&quot;27:9-28:0&quot;&gt;이렇게 설정하면, ssh 터널링을 이용해서 Main탭에서 설정한 RDS 엔드포인트로 가도록 해줍니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>DATABASE</category>
      <category>AWS</category>
      <category>bastion host</category>
      <category>DBeaver</category>
      <category>ec2 터널링</category>
      <category>rds 연결</category>
      <category>SSH 터널링</category>
      <category>VPC</category>
      <author>Aytekin</author>
      <guid isPermaLink="true">https://aytekin.tistory.com/148</guid>
      <comments>https://aytekin.tistory.com/148#entry148comment</comments>
      <pubDate>Fri, 20 Jun 2025 16:31:48 +0900</pubDate>
    </item>
    <item>
      <title>[flutter 개발환경 세팅] 코드 리펙토링 | Code Action | ctrl + .</title>
      <link>https://aytekin.tistory.com/147</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;flutter 개발하다보면 이미 만든 widget을 Center로 감싸거나 Container로 감싸야 하는 등 수정할 상황이 생긴다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때마다 들여쓰기 하나하나 확인해가면서 부모 레벨에서 Container를 추가하는건 굉장히 힘들고 에너지가 많이 든다.&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;Code Action를 이용하면 위의 작업을 손쉽게 끝낼 수 있다.&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;ctrl + . 을 입력하면 아래와 같이 뜬다.&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;450&quot; data-origin-height=&quot;565&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CBNI3/btsOd93fP2h/h7sTdCZjYR0e2Ge32rjUhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CBNI3/btsOd93fP2h/h7sTdCZjYR0e2Ge32rjUhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CBNI3/btsOd93fP2h/h7sTdCZjYR0e2Ge32rjUhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCBNI3%2FbtsOd93fP2h%2Fh7sTdCZjYR0e2Ge32rjUhk%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;450&quot; height=&quot;565&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;565&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wrap with Column, Wrap with Container 등등 감싸고 싶은 위젯을 선택해서 감싸주면 끝&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심지어 Wrap with widget은 커스텀 위젯을 내가 사용할 수 있도록 틀을 만들어준다.&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;플러터 개발자 경험(flutter Developer Experience, DX) 극 상승!!!&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;</description>
      <category>플러터 Flutter</category>
      <category>codeaction</category>
      <category>DX</category>
      <category>flutter</category>
      <category>개발자경험</category>
      <category>세팅</category>
      <category>플러터</category>
      <category>환경설정</category>
      <category>환경세팅</category>
      <author>Aytekin</author>
      <guid isPermaLink="true">https://aytekin.tistory.com/147</guid>
      <comments>https://aytekin.tistory.com/147#entry147comment</comments>
      <pubDate>Wed, 28 May 2025 09:49:02 +0900</pubDate>
    </item>
    <item>
      <title>[flutter 개발환경 세팅] vscode 들여쓰기 UI 설정하기 - previewFlutterUiGuides</title>
      <link>https://aytekin.tistory.com/146</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;command pallet &amp;gt;&amp;gt;&amp;gt; [ctrl + shift + p]&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;497&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqu1Mi/btsOfrV21VY/Z7Pd2rW5K0wrEEZZSTpQb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqu1Mi/btsOfrV21VY/Z7Pd2rW5K0wrEEZZSTpQb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqu1Mi/btsOfrV21VY/Z7Pd2rW5K0wrEEZZSTpQb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbqu1Mi%2FbtsOfrV21VY%2FZ7Pd2rW5K0wrEEZZSTpQb0%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;596&quot; height=&quot;497&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;497&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;open user settings(JSON)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;121&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Qm48B/btsOeGlUEWJ/KyxtOFvydtdbwaUFg9ce61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Qm48B/btsOeGlUEWJ/KyxtOFvydtdbwaUFg9ce61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Qm48B/btsOeGlUEWJ/KyxtOFvydtdbwaUFg9ce61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQm48B%2FbtsOeGlUEWJ%2FKyxtOFvydtdbwaUFg9ce61%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;632&quot; height=&quot;121&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;121&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;&quot;dart.previewFlutterUiGuides&quot;: true&lt;/b&gt;&lt;/span&gt; 추가&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;143&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ecyisG/btsOf1iiVVY/YFYwfH6Nh44kjGB8h4o55k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ecyisG/btsOf1iiVVY/YFYwfH6Nh44kjGB8h4o55k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ecyisG/btsOf1iiVVY/YFYwfH6Nh44kjGB8h4o55k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FecyisG%2FbtsOf1iiVVY%2FYFYwfH6Nh44kjGB8h4o55k%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;406&quot; height=&quot;143&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;143&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p 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;549&quot; data-origin-height=&quot;645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pBRzh/btsOea12pgX/UvvX2L188JeqYg7AzB1jbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pBRzh/btsOea12pgX/UvvX2L188JeqYg7AzB1jbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pBRzh/btsOea12pgX/UvvX2L188JeqYg7AzB1jbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpBRzh%2FbtsOea12pgX%2FUvvX2L188JeqYg7AzB1jbk%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;549&quot; height=&quot;645&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;645&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;/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;개발자 경험(developer experience, DX) 극 상승!&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;</description>
      <category>플러터 Flutter</category>
      <category>dart</category>
      <category>DX</category>
      <category>flutter</category>
      <category>settings</category>
      <category>UI</category>
      <category>vscode</category>
      <category>개발자경험</category>
      <author>Aytekin</author>
      <guid isPermaLink="true">https://aytekin.tistory.com/146</guid>
      <comments>https://aytekin.tistory.com/146#entry146comment</comments>
      <pubDate>Wed, 28 May 2025 09:36:23 +0900</pubDate>
    </item>
    <item>
      <title>[django] Django Admin 메뉴 순서 커스터마이징 &amp;ndash; 공백 기반 정렬 방식</title>
      <link>https://aytekin.tistory.com/145</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Django 프로젝트가 커지면 Admin 페이지에 등록된 모델 수가 많아지고, 이를 보기 좋게 정렬하고 싶은 상황이 생깁니다. 기본적으로 Django Admin은 &lt;b&gt;모델 이름을 알파벳순으로&lt;/b&gt; 정렬하지만, 실제 사용 시에는 &lt;b&gt;업무 플로우나 중요도에 따라 직접 메뉴 순서를 조정하고 싶은 경우&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;이번 포스트에서는 verbose_name_plural의 &lt;b&gt;앞쪽 공백 수를 활용해 원하는 정렬 순서를 만드는 방법&lt;/b&gt;과 함께, 이를 &lt;b&gt;상수로 정리해서 관리하는 패턴&lt;/b&gt;을 소개합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  문제 상황&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Django Admin은 모델 이름의 알파벳 순서로 메뉴를 정렬합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 아래와 같은 모델들을 관리할 때는 업무에 따라 &lt;b&gt;특정 모델을 위쪽에 배치하고 싶은 경우&lt;/b&gt;가 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 해결 방법: 공백 수를 활용한 정렬 순서 제어&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델 메뉴 이름에 &lt;b&gt;공백을 추가하면 알파벳 정렬 기준에서 더 먼저 배치&lt;/b&gt;됩니다. 이 특성을 이용해서 정렬 우선순위를 설정합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;# model_order.py 또는 constants.py 등에 공통 정의
# ⚠️ 공백이 많을수록 Admin 메뉴 상단에 배치됩니다.

ASSIGNMECHANIC = &quot;               담당자 별 배정내역&quot;              # 15칸 공백
OTHERMECHANICSASSIGNEDMODEL = &quot;              다른기사 배정내역&quot;     # 14칸 공백
HISTORYMODEL = &quot;             과거조치내역 조회&quot;                    # 13칸 공백
ASSIGNEDMODEL = &quot;            전체배정내역 조회&quot;                    # 12칸 공백
...
RECEIVABLESDETAILS = &quot;미수금알림톡&quot;                               # 공백 없음
&lt;/code&gt;&lt;/pre&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;model_order.py 또는 constancts.py에 정의하여서 사용해도 되고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;models.py 최상단에 상수를 선언해서 사용해도 됩니다.(제가 사용한 방법입니다.)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 모델 클래스에 적용 예시&lt;/h2&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from django.db import models
from .model_order import ASSIGNMECHANIC

class AssignMechanic(models.Model):
    # 필드 정의 생략

    class Meta:
        verbose_name = ASSIGNMECHANIC
        verbose_name_plural = ASSIGNMECHANIC
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 모델들도 동일한 방식으로 아래와 같이 구성합니다:&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;class HistoryModel(models.Model):
    # ...

    class Meta:
        verbose_name = HISTORYMODEL
        verbose_name_plural = HISTORYMODEL
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 Django Admin의 사이드바에서 원하는 순서대로 모델이 정렬됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&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;background-color: #ffc1c8;&quot;&gt;&lt;b&gt;띄어쓰기(공백)&lt;/b&gt;&lt;/span&gt; 개수가 많을수록 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;&lt;b&gt;메뉴 상단에 먼저 배치&lt;/b&gt;&lt;/span&gt;됩니다. &lt;br /&gt;* Django Admin 또는 커스텀 UI에서 메뉴 순서를 제어하기 위한 규칙입니다. &lt;br /&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;verbose_name_plural&lt;/td&gt;
&lt;td&gt;Admin 메뉴에서 표시될 이름&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;상수 사용&lt;/td&gt;
&lt;td&gt;여러 모델에서 재사용 가능하며 유지보수에 유리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&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;읽어주셔서 감사합니다.&lt;/p&gt;</description>
      <category>파이썬 python/django</category>
      <category>admin</category>
      <category>Django</category>
      <category>django admin</category>
      <category>django model order</category>
      <category>model order</category>
      <category>유지보수</category>
      <category>장고</category>
      <category>파이썬</category>
      <author>Aytekin</author>
      <guid isPermaLink="true">https://aytekin.tistory.com/145</guid>
      <comments>https://aytekin.tistory.com/145#entry145comment</comments>
      <pubDate>Thu, 17 Apr 2025 11:35:23 +0900</pubDate>
    </item>
    <item>
      <title>[django] Django Admin Inline템플릿에 Custom Context 안전하게 전달하는 방법</title>
      <link>https://aytekin.tistory.com/144</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;mdash; get_inline_instances()로 커스텀 컨텍스트(custom context) 주입하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Django Admin은 뛰어난 기능을 제공하지만, 종종 커스터마이징에는 제한이 따릅니다. 특히, Inline에 custom template을 적용하고, 여기에 &lt;b&gt;개별 객체 정보를 포함한 동적 context를 전달&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;이 글에서는 get_inline_instances()를 활용해 Django Admin Inline에 안전하게 custom context를 전달하는 가장 실용적인 방법을 소개합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&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;Django Admin의 Inline에 custom template을 사용&lt;/li&gt;
&lt;li&gt;Inline 템플릿 내에서 동적 정보를 안전하게 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  핵심 전략&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Django는 Inline을 렌더링할 때 내부적으로 inline_admin_formset.opts를 통해 Inline 클래스 인스턴스를 템플릿에 전달합니다.&lt;br /&gt;우리는 이 opts 객체에 커스텀 context를 직접 주입함으로써 템플릿에서 데이터를 활용할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  1단계: Inline 클래스 정의&lt;/h2&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;# admin.py

class MaintainanceOfficeGroupInline(admin.TabularInline):
    model = MaintainanceGroup
    extra = 0
    template = 'custom_inline_maintainance_office_group.html'
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;custom_inline_maintainance_office_group.html이라는 커스텀 템플릿 파일을 만듭니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일의 경로는 settings.py에서 TEMPLATES에 등록된 디랙토리에 있어야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  2단계: Inline을 포함하는 Admin 클래스에서 context 주입&lt;/h2&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;class MaintainanceAdmin(admin.ModelAdmin):
    inlines = [MaintainanceOfficeGroupInline]

    def get_inline_instances(self, request, obj=None):
        inline_instances = super().get_inline_instances(request, obj)

        for inline in inline_instances:
            if isinstance(inline, MaintainanceOfficeGroupInline):
                # 커스텀 context를 직접 주입
                inline.custom_context = {
                    'obj': obj,
                    'site': getattr(obj, 'site', None),
                    'today': datetime.date.today(),
                    'mechanic': getattr(obj, 'assigned_mechanic', None),
                }

        return inline_instances
&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;정의한 MaintainanceOfficeGroupInline클래스를 연결하고, get_inline_instances() 객체를 통해 커스텀 정보를 넘겨줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 custom_context는 템플릿에서 접근 가능한 안전한 컨텍스트 객체로 작동합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  3단계: 커스텀 Inline 템플릿에서 context 활용&lt;/h2&gt;
&lt;pre class=&quot;django&quot;&gt;&lt;code&gt;{# templates/custom_inline_maintainance_office_group.html #}

{% if inline_admin_formset.opts.custom_context %}
    &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;오늘 날짜:&amp;lt;/strong&amp;gt; {{ inline_admin_formset.opts.custom_context.today }}&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;사이트:&amp;lt;/strong&amp;gt; {{ inline_admin_formset.opts.custom_context.site }}&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;담당자:&amp;lt;/strong&amp;gt; {{ inline_admin_formset.opts.custom_context.mechanic }}&amp;lt;/p&amp;gt;
{% endif %}

{{ inline_admin_formset }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inline_admin_formset.opts는 해당 inline 클래스의 인스턴스를 참조하므로, 우리가 주입한 custom_context에 직접 접근할 수 있습니다.&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;b&gt;{{ inline_admin_formset.opts }} == MaintainanceOfficeGroupInline의 객체(object)&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⚠️ 주의사항&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;obj가 None인 경우&lt;/td&gt;
&lt;td&gt;Admin의 add 모드에서는 obj가 없을 수 있으므로, .get()이나 getattr()로 안전하게 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이름 충돌 방지&lt;/td&gt;
&lt;td&gt;custom_context는 Django 내부에서 사용하지 않는 속성이므로 충돌 위험은 적지만, 명확한 네이밍 유지 추천&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Django 버전 의존성&lt;/td&gt;
&lt;td&gt;이 방식은 Django의 렌더링 방식에 어느 정도 의존하므로 버전 업 시 템플릿 구조 변경 확인 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Django Admin Inline에 custom context를 전달하고 싶다면, get_inline_instances()를 활용한 인스턴스 주입 방식이 가장 안정적이고 Django의 흐름에도 잘 맞습니다.&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;이 기법을 통해 템플릿에서 동적 정보를 활용할 수 있으며, 다양한 사용자 맞춤형 UI 구성에 유용하게 활용할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  팁&lt;/b&gt;&lt;br /&gt;Inline마다 서로 다른 데이터를 전달하고 싶다면, 각 Inline 클래스별로 다른 custom_context 값을 설정하면 됩니다.&lt;/p&gt;</description>
      <category>파이썬 python/django</category>
      <category>custom template</category>
      <category>Django</category>
      <category>django admin</category>
      <category>django custom context</category>
      <category>django어드민</category>
      <category>장고</category>
      <author>Aytekin</author>
      <guid isPermaLink="true">https://aytekin.tistory.com/144</guid>
      <comments>https://aytekin.tistory.com/144#entry144comment</comments>
      <pubDate>Wed, 16 Apr 2025 17:31:24 +0900</pubDate>
    </item>
    <item>
      <title>[django] database is locked - 원인파악 | 해결방안</title>
      <link>https://aytekin.tistory.com/143</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 원인 파악&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Django에서 &quot;database is locked&quot; 오류는 SQLite3 데이터베이스에 현재 다른 프로세스가 잠금을 걸고 있는 상태에서 데이터베이스 작업을 시도할 때 발생합니다. 이 문제는 여러 가지 이유로 발생할 수 있습니다:&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;: SQLite3 데이터베이스는 낮은 트래픽에서 중간 트래픽까지를 처리하도록 설계되었습니다. 높은 수준의 동시 쓰기는 데이터베이스가 잠기는 결과를 초래할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장시간 실행되는 트랜잭션&lt;/b&gt;: 트랜잭션이 완료되는 데 오랜 시간이 걸리면, 데이터베이스에 잠금을 걸어 다른 트랜잭션이 진행되지 않게 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;파일 잠금 문제&lt;/b&gt;: SQLite는 동시성을 관리하기 위해 파일 잠금을 사용합니다. 파일 시스템 잠금 메커니즘에 문제가 있으면 데이터베이스가 잠길 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;연결 풀링&lt;/b&gt;: SQLite를 사용할 때 연결 풀링을 사용하면 여러 연결이 동시에 데이터베이스에 쓰기 작업을 시도할 수 있습니다.&lt;/li&gt;
&lt;/ul&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&gt;&lt;b&gt;적절한 데이터베이스 트랜잭션 사용&lt;/b&gt; :&amp;nbsp;트랜잭션을 가능한 짧게 유지하세요. 트랜잭션을 빠르게 커밋하거나 롤백하여 데이터베이스 잠금을 해제합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1730858382308&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from django.db import transaction

with transaction.atomic():
    # 데이터베이스 작업 수행&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&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; : SQLite3는 잠금을 얻으려고 시도할 때 대기할 시간을 설정할 수 있습니다. 이 설정은 Django 설정에서 구성할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1730858392342&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'mydatabase',
        'OPTIONS': {
            'timeout': 20,  # 타임아웃을 20초로 설정, default=5초
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&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;/li&gt;
&lt;li&gt;&lt;b&gt;다른 데이터베이스 백엔드 사용 :&lt;/b&gt; 애플리케이션이 높은 트래픽을 경험하거나 강력한 동시성 지원이 필요한 경우, PostgreSQL 또는 MySQL과 같은 더 나은 동시성 지원을 제공하는 다른 데이터베이스 백엔드를 사용하는 것을 고려하세요.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;파일 기반 잠금 사용 :&lt;/b&gt; 운영 체제와 파일 시스템이 SQLite에서 요구하는 파일 기반 잠금 메커니즘을 지원하는지 확인하세요.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;연결 풀링 관리 :&lt;/b&gt; SQLite와 연결 풀링 메커니즘을 사용할 때는, 하나의 쓰기 작업만 수행될 수 있도록 설정하거나 연결 풀링을 피하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&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;Sqlite3가 동시작업(concurrency)이 불가하여 앞선 작업이 진행중일 땐, 데이터베이스를 잠궈(lock)놓습니다.&lt;/li&gt;
&lt;li&gt;이 때문에 sqlite3에는 time-lock이라는 설정값이 있다.&lt;/li&gt;
&lt;li&gt;설정된 time-lock 시간을 넘어갈동안 앞선 작업이 끝나지 않을 경우 &quot;database is locked&quot;의 오류를 던지게 됩니다.&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;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;timeout 설정값 변경(default=5초)&lt;/li&gt;
&lt;li&gt;장시간 쿼리 최적화&lt;/li&gt;
&lt;li&gt;(최후의 방법)대체 데이터베이스 백엔드로 교체(ex. MySQL, PostgreSQL, Oracle, MongoDB...)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB를 교체하는건 큰 작업이므로 처음 프로젝트를 설계할 시 DB 선정에도 신중에 신중을 가해야 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[참고]&lt;/p&gt;
&lt;figure id=&quot;og_1730858123228&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;Databases | Django documentation&quot; data-og-description=&quot;The web framework for perfectionists with deadlines.&quot; data-og-host=&quot;docs.djangoproject.com&quot; data-og-source-url=&quot;https://docs.djangoproject.com/en/5.1/ref/databases/#database-is-locked-errors&quot; data-og-url=&quot;https://docs.djangoproject.com/en/5.1/ref/databases/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eoE0gH/hyXs4cWb8E/jyZ7TxwAm6ZUGqFNMk1qu0/img.png?width=1200&amp;amp;height=546&amp;amp;face=0_0_1200_546&quot;&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/5.1/ref/databases/#database-is-locked-errors&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.djangoproject.com/en/5.1/ref/databases/#database-is-locked-errors&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eoE0gH/hyXs4cWb8E/jyZ7TxwAm6ZUGqFNMk1qu0/img.png?width=1200&amp;amp;height=546&amp;amp;face=0_0_1200_546');&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;Databases | Django documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The web framework for perfectionists with deadlines.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.djangoproject.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>파이썬 python/django</category>
      <category>Django</category>
      <category>error</category>
      <category>sqlite3</category>
      <category>오류해결</category>
      <category>장고</category>
      <author>Aytekin</author>
      <guid isPermaLink="true">https://aytekin.tistory.com/143</guid>
      <comments>https://aytekin.tistory.com/143#entry143comment</comments>
      <pubDate>Wed, 6 Nov 2024 11:02:29 +0900</pubDate>
    </item>
    <item>
      <title>[파이썬] 천 단위로 쉼표 표기하는 초간단 방법 : format, f-string 이용</title>
      <link>https://aytekin.tistory.com/142</link>
      <description>&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;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;770000원&lt;/span&gt;&lt;/b&gt; 보다&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;770,000원&lt;/span&gt;&lt;/b&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;이번 글에서는 이렇게 천 단위로 쉼표를 표시해주는 간단한 파이썬 코드를 공유한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.&lt;span style=&quot;background-color: #dddddd;&quot;&gt; format&lt;/span&gt; 이용&lt;/h2&gt;
&lt;pre id=&quot;code_1728369557119&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 금액을 천 단위로 쉼표를 찍어서 표시하는 함수
def format_amount(amount):
    return &quot;{:,}&quot;.format(amount)

# 예시 금액
amount = 770000
formatted_amount = format_amount(amount)

print(formatted_amount)  # 출력: 770,000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.&lt;span style=&quot;background-color: #dddddd;&quot;&gt; f-string&lt;/span&gt;이용&lt;/h2&gt;
&lt;pre id=&quot;code_1728369576934&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 금액을 천 단위로 쉼표를 찍어서 표시하는 함수
def format_amount(amount):
    return f&quot;{amount:,}&quot;

# 예시 금액
amount = 770000
formatted_amount = format_amount(amount)

print(formatted_amount)  # 출력: 770,000&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;p data-ke-size=&quot;size16&quot;&gt;천 단위로 쉼표를 찍는&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&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;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;041&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/041.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/041.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감사합니다~!&lt;/p&gt;</description>
      <category>파이썬 python</category>
      <category>f-string</category>
      <category>format</category>
      <category>쉼표</category>
      <category>천단위</category>
      <category>통화단위</category>
      <category>파이썬</category>
      <author>Aytekin</author>
      <guid isPermaLink="true">https://aytekin.tistory.com/142</guid>
      <comments>https://aytekin.tistory.com/142#entry142comment</comments>
      <pubDate>Tue, 8 Oct 2024 15:42:14 +0900</pubDate>
    </item>
    <item>
      <title>[django] model object update 방법 정리</title>
      <link>https://aytekin.tistory.com/141</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;546&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/du6kKs/btsJVH2XAbJ/Txjm0C4pe33eZ59prOi3Mk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/du6kKs/btsJVH2XAbJ/Txjm0C4pe33eZ59prOi3Mk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/du6kKs/btsJVH2XAbJ/Txjm0C4pe33eZ59prOi3Mk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdu6kKs%2FbtsJVH2XAbJ%2FTxjm0C4pe33eZ59prOi3Mk%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;1200&quot; height=&quot;546&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;546&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;django에서는 모델 객체를 업데이트 하는 방법이 여러가지가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;쓸때마다 이거였지 생각은 나는데 정확하지 않아 계속 찾아보고 하는 과정이 반복되어&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번 기회에 정리하고자 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;django developer들에게 조금이나마 도움이 되길 바란다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;https://tistory1.daumcdn.net/tistory/4708314/skin/images/jquery.toc.js&quot;&gt;&lt;/script&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 id=&quot;toc&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script type=&quot;text/javascript&quot;&gt; $(function(){ $(&quot;#toc&quot;).toc( {content: &quot;.tt_article_useless_p_margin&quot;, headings: &quot;h2,h3,h4&quot; , top: -90, isBlink : true, blinkColor : '#21B9DE' } ) }); &lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Queryset.update()&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&quot;django model update&quot;를 검색했을 때 가장 많이 나오는 방법이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;&lt;b&gt;즉시&lt;/b&gt;&lt;/span&gt; 적용(applied instantly)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;obj.save() 필요 없음&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;update()매서드를 호출하는 순간 데이터가 변경된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;save()보다 더 빠르다(Queryset의 특징인 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;laziness가 적용되지 않는다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;외부 테이블의 필드는&amp;nbsp;수정 불가&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;해당 테이블의 데이터필드만 수정 가능하고 외래키로 수정은 불가하다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728027227854&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; Entry.objects.update(blog__name=&quot;foo&quot;)  # Won't work!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;당연하지만 외부테이블로 filtering한 후 원래 테이블의 필드를 수정하는것은 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728027330959&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; Entry.objects.filter(blog__id=1).update(comments_on=True) # Work!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;multiple object, multiple fields 모두 가능&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;multiple objects update&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728026933574&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; Entry.objects.filter(pub_date__year=2010).update(comments_on=False)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;publish_date가 2010년인 데이터 모두에 대해 comments_on 필드를 False로 변경해준다.&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;span style=&quot;color: #333333; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;multiple objects + multiple fields update&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728026976159&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Entry.objects.filter(pub_date__year=2010).update(
...     comments_on=False, headline=&quot;This is old&quot;
... )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt; publish_date가 2010년인 데이터 모두에 대해 comments_on 필드를 False로 headline필드를 &quot;This is old&quot;로 변경해준다.&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start; background-color: #ffc1c8;&quot;&gt; *&lt;b&gt;하나의 데이터만 업데이트 하고 싶은 경우&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;id 로 필터링 후 update 매서드 사용하면 유용함. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;filter매서드가 리스트로 객체를 주기 때문에 뭔가 비효율적으로 보이지만 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;지금까지 사용한 경험으로는 이 방법이 제일 깔끔한것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;return값은 변경된 데이터의 개수(updated object count)&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1728027439637&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; Entry.objects.filter(id=64).update(comments_on=True)
1

&amp;gt;&amp;gt;&amp;gt; Entry.objects.filter(slug=&quot;nonexistent-slug&quot;).update(comments_on=True)
0

&amp;gt;&amp;gt;&amp;gt; Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
132&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;memory로 가져오지 않고 db에서 바로 작업(SQL level)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공식문서 설명에 따르면 update()매서드는 서버의 memory로 객체를 가져오지 않고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; background-color: #ffc1c8;&quot;&gt;db내(SQL level)에서 처리를 끝낸다.&lt;/span&gt;(laziness X)&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; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;반면 save()매서드의 경우 서버의 memory로 들고온다.(laziness O)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;단순히 값만 변경하는 경우는 update를 사용하는 것이 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;e = Entry.objects.get(id=10)
e.comments_on = False
e.save()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이거보다는&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #0c3c26; text-align: start;&quot;&gt;
&lt;pre class=&quot;jboss-cli&quot; style=&quot;color: #000000;&quot;&gt;&lt;code&gt;Entry.objects.filter(id=10).update(comments_on=False)
&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이렇게 사용하는 것이 메모리를 효율적으로 사용하는 방법이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;정리&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;SQL level에서 데이터 업데이트를 한다. =&amp;gt; 서버 메모리 사용하지 않음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하나의 데이터만 update할 경우 id로 필터링 후 update사용&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;queryset.update() &amp;gt;&amp;gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/5.1/ref/models/querysets/#update&quot;&gt;https://docs.djangoproject.com/en/5.1/ref/models/querysets/#update&lt;/a&gt; &lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1728026842694&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;QuerySet API reference | Django documentation&quot; data-og-description=&quot;The web framework for perfectionists with deadlines.&quot; data-og-host=&quot;docs.djangoproject.com&quot; data-og-source-url=&quot;https://docs.djangoproject.com/en/5.1/ref/models/querysets/#update&quot; data-og-url=&quot;https://docs.djangoproject.com/en/5.1/ref/models/querysets/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wXf4Y/hyXd8k7bsF/RwTy9gGXH26Bc8lUQFobx1/img.png?width=1200&amp;amp;height=546&amp;amp;face=0_0_1200_546&quot;&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/5.1/ref/models/querysets/#update&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.djangoproject.com/en/5.1/ref/models/querysets/#update&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wXf4Y/hyXd8k7bsF/RwTy9gGXH26Bc8lUQFobx1/img.png?width=1200&amp;amp;height=546&amp;amp;face=0_0_1200_546');&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;QuerySet API reference | Django documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The web framework for perfectionists with deadlines.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.djangoproject.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;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Queryset.bulk_update(objs, fields, batch_size=None)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;여러객체에 대해 동시에 update하고자 할 때 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결국 내부적으로 queryset.update()를 사용한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(github link : &lt;a href=&quot;https://github.com/django/django/blob/main/django/db/models/query.py#L904&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/django/django/blob/main/django/db/models/query.py#L904&lt;/a&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; background-color: #ffc1c8;&quot;&gt;업데이트 로직이 복잡한 경우 사용하면 좋을 듯 하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그런 경우가 아니면 update()매서드가 나아보인다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사용법&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;첫번째 인자로 변경하고자 하는 objs리스트&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;두번째 인자로 변경하고자 하는 field 리스트&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;세번째는 batch_size로 한번에 얼마나 많은 데이터를 update할지&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;python&quot; style=&quot;color: #000000; text-align: start;&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;objs_to_update = MyModel.objects.filter(id__in=[1, 2, 3])
for obj in objs_to_update:
    obj.field1 = 'New Value'
    obj.field2 = 42
    obj.field3 = datetime.now()

MyModel.objects.bulk_update(objs_to_update, ['field1', 'field2', 'field3'])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;bulk_update를 사용하기 좋은 조건&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. update시 다른 로직을 추가할 경우 (update()의 경우엔 다른 로직을 추가하기 힘들다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. 이미 모델 객체가 메모리에 올라가있는 경우에(already fetched case) 쿼리를 최소화하고 싶을때&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;queryset.bulk_update() &amp;gt;&amp;gt; &lt;a href=&quot;https://docs.djangoproject.com/en/5.1/ref/models/querysets/#bulk-update&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.djangoproject.com/en/5.1/ref/models/querysets/#bulk-update&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1728029027665&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;QuerySet API reference | Django documentation&quot; data-og-description=&quot;The web framework for perfectionists with deadlines.&quot; data-og-host=&quot;docs.djangoproject.com&quot; data-og-source-url=&quot;https://docs.djangoproject.com/en/5.1/ref/models/querysets/#bulk-update&quot; data-og-url=&quot;https://docs.djangoproject.com/en/5.1/ref/models/querysets/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/caC2Qc/hyXaHbB3Eu/7FBEa5KdAK2PAdI3CK49ck/img.png?width=1200&amp;amp;height=546&amp;amp;face=0_0_1200_546&quot;&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/5.1/ref/models/querysets/#bulk-update&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.djangoproject.com/en/5.1/ref/models/querysets/#bulk-update&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/caC2Qc/hyXaHbB3Eu/7FBEa5KdAK2PAdI3CK49ck/img.png?width=1200&amp;amp;height=546&amp;amp;face=0_0_1200_546');&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;QuerySet API reference | Django documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The web framework for perfectionists with deadlines.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.djangoproject.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;h2 data-ke-size=&quot;size26&quot;&gt;Model.save()&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 원초적인(?) 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;db에서 데이터를 가져와 객체로 메모리 할당 후 다시 DB에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;&lt;b&gt;update매서드보다 더 느리고 서버 메모리 차원에서 비효율적이다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;UPDATE vs INSERT 장고는 어떻게 구분할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 update할때 뿐만아니라 insert할때도 save()는 사용할 수 있다.&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;근데 어떻게 장고는 이걸 구분하느냐?!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본키(Primary key)에 해당하는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 있으면 UPDATE&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 없으면 INSERT&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;&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;Saving objects =&amp;gt; &lt;a href=&quot;https://docs.djangoproject.com/en/5.1/ref/models/instances/#saving-objects&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.djangoproject.com/en/5.1/ref/models/instances/#saving-objects&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1728029297943&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;Model instance reference | Django documentation&quot; data-og-description=&quot;The web framework for perfectionists with deadlines.&quot; data-og-host=&quot;docs.djangoproject.com&quot; data-og-source-url=&quot;https://docs.djangoproject.com/en/5.1/ref/models/instances/#saving-objects&quot; data-og-url=&quot;https://docs.djangoproject.com/en/5.1/ref/models/instances/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/OXOZ7/hyXaw1554m/Aj1xRC3kL3eI2qEE6lrkv0/img.png?width=1200&amp;amp;height=546&amp;amp;face=0_0_1200_546&quot;&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/5.1/ref/models/instances/#saving-objects&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.djangoproject.com/en/5.1/ref/models/instances/#saving-objects&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/OXOZ7/hyXaw1554m/Aj1xRC3kL3eI2qEE6lrkv0/img.png?width=1200&amp;amp;height=546&amp;amp;face=0_0_1200_546');&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;Model instance reference | Django documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The web framework for perfectionists with deadlines.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.djangoproject.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;h2 data-ke-size=&quot;size26&quot;&gt;update_or_create(defaults=None, create_defaults=None)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말 그대로 업데이트 하거나 해당 데이터가 없으면 만들어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;default(dict) : 업데이트 하고자 하는 데이터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;create_default : 생성할 때 사용하는 데이터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* create_default를 설정하지 않으면 default로 생성(create)까지 한다.&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;get_or_create()와 마찬가지로 return값은 (object, is_created) 튜플을 준다.&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;h3 data-ke-size=&quot;size23&quot;&gt;사용법&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;obj, created = Person.objects.update_or_create(
    first_name=&quot;John&quot;,
    last_name=&quot;Lennon&quot;,
    defaults={&quot;first_name&quot;: &quot;Bob&quot;},
    create_defaults={&quot;first_name&quot;: &quot;Bob&quot;, &quot;birthday&quot;: date(1940, 10, 9)},
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;John Lennon&quot;&lt;/b&gt;이라는 사람이 있으면 &lt;b&gt;&quot;Bob Lennon&quot;&lt;/b&gt;으로 &lt;b&gt;수정&lt;/b&gt;하고 없으면 &lt;b&gt;&quot;Bob&quot;, (생일 1940,10,9)&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;/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;gt; &lt;a href=&quot;https://docs.djangoproject.com/en/5.1/ref/models/querysets/#update-or-create&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.djangoproject.com/en/5.1/ref/models/querysets/#update-or-create&lt;/a&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;&lt;a href=&quot;https://velog.io/@fregataa/django-QuerySet.update-vs-QuerySet.bulkupdate-vs-Model.save&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@fregataa/django-QuerySet.update-vs-QuerySet.bulkupdate-vs-Model.save&lt;/a&gt;&lt;/p&gt;</description>
      <category>파이썬 python/django</category>
      <category>Django</category>
      <category>model</category>
      <category>queryset</category>
      <category>update</category>
      <category>장고</category>
      <author>Aytekin</author>
      <guid isPermaLink="true">https://aytekin.tistory.com/141</guid>
      <comments>https://aytekin.tistory.com/141#entry141comment</comments>
      <pubDate>Fri, 4 Oct 2024 17:20:03 +0900</pubDate>
    </item>
    <item>
      <title>[django] django로 포트포워딩(port forwarding)하는 방법 (w. middleware)</title>
      <link>https://aytekin.tistory.com/131</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;django로 포트포워딩(port forwarding)하는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Django 애플리케이션에서 클라이언트가 어떤 도메인을 통해 접근했는지를 확인하고, 해당 도메인에 따라 다른 페이지로 리다이렉션하는 방법&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;1767&quot; data-origin-height=&quot;835&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpkc1t/btsIM7BxlwP/yvB0qrf4sAcSKo3EvT8KvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpkc1t/btsIM7BxlwP/yvB0qrf4sAcSKo3EvT8KvK/img.png&quot; data-alt=&quot;배포 관점에서 그려본 프로젝트 흐름&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpkc1t/btsIM7BxlwP/yvB0qrf4sAcSKo3EvT8KvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdpkc1t%2FbtsIM7BxlwP%2FyvB0qrf4sAcSKo3EvT8KvK%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;1767&quot; height=&quot;835&quot; data-origin-width=&quot;1767&quot; data-origin-height=&quot;835&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;배포 관점에서 그려본 프로젝트 흐름&lt;/figcaption&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;/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;처음에는 django admin페이지를 이용한 서비스를 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 back과 front모두 django안에서 해결 할 수 있었다.&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;b&gt;Route53&lt;/b&gt;을 이용해서&lt;b&gt; 호스팅&lt;/b&gt;을 했고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;subdomain&lt;/b&gt;으로 first를 붙여 (예) first.webname.net으로 배포했었다.&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;b&gt;탄력적 IP&lt;/b&gt;를 이용해서 내가 배포한 EC2에 &lt;b&gt;고정적&lt;/b&gt;으로 접근할 수 있도록 설정해두었고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;django 웹앱을 포트번호 8000번을 이용했다.&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;여기서&amp;nbsp;&lt;b&gt;second.webname.net &lt;/b&gt;이라는 새로운 subdomain을&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React프로젝트로 연결시켜 줄 방법이 필요했다.&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;React는 포트번호 3000번을 사용했고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 &lt;b&gt;django를 통해서 다 관리하기&lt;/b&gt;를 원했기에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;django로 포트포워딩&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;클라이언트 입장에서는 포트 8000번으로 들어갔다가 다시 django가 안내하는 포트3000번으로 가야하는 긴 여행을 해야한다.&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;사실 nginx같은 웹서버를 통해 hosting주소에 따라 포트를 나눠줄 수 있을것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nignx가 나에겐 아직 익숙하지 않아서 비교적 나에게 익숙한 환경인 django 안에서 해결해보았다.&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;일단, second.webname.net을 브라우저에 입력해서 들어가면 first.webname.net과 마찬가지로 django의 index페이지가 열릴것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 나는 host에 따라서 first는 django로, second는 react로 보내고 싶은것이다.&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;코드를 살펴보자.&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;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 미들웨어 생성&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;
&lt;pre id=&quot;code_1721883721108&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#`myapp/middleware.py` 파일 생성

from django.shortcuts import redirect
from DRAXASServer.settings import SERVICE_DOMAIN_URL, SERVICE_TO_URL

class HostRedirectMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        host = request.get_host()

        if host == HOST_DOMAIN:
            return redirect(SERVICE_TO_URL)

        response = self.get_response(request)
        return response&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;redirect할 대상 페이지 주소를 &quot;SERVICE_TO_URL&quot;이라는 변수명으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분하고자 하는 host 도메인 주소를 &quot;HOST_DOMAIN&quot;이라는 변수명으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;settings.py에 선언했다.&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;&lt;b&gt;2. 미들웨어 등록&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;작성한 미들웨어를 Django 프로젝트의 설정 파일에 등록&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721883907970&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# settings.py

MIDDLEWARE = [
    ...
    'myapp.middleware.HostRedirectMiddleware',  # 미들웨어 등록
    ...
]

...

SERVICE_TO_URL = &quot;https://first.webname.net:3000/&quot;
HOST_DOMAIN = &quot;second.webname.net&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;second.webname.net으로 django에 접근하면 django는 https://first.webname.net:3000로 보내준다.&lt;/p&gt;</description>
      <category>파이썬 python/django</category>
      <category>Django</category>
      <category>EC2</category>
      <category>Route53</category>
      <category>미들웨어</category>
      <category>어무해</category>
      <category>포트포워딩</category>
      <category>호스팅</category>
      <author>Aytekin</author>
      <guid isPermaLink="true">https://aytekin.tistory.com/131</guid>
      <comments>https://aytekin.tistory.com/131#entry131comment</comments>
      <pubDate>Thu, 25 Jul 2024 14:29:31 +0900</pubDate>
    </item>
  </channel>
</rss>