<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: JustJinoIT</title>
    <description>The latest articles on DEV Community by JustJinoIT (@justjinoit).</description>
    <link>https://dev.to/justjinoit</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3971008%2F36558f2c-e9c9-4b2f-bdf0-caeabca94863.png</url>
      <title>DEV Community: JustJinoIT</title>
      <link>https://dev.to/justjinoit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/justjinoit"/>
    <language>en</language>
    <item>
      <title>Removing API Keys from Git History: BFG + Force Push (A Security Incident Response)</title>
      <dc:creator>JustJinoIT</dc:creator>
      <pubDate>Sat, 06 Jun 2026 10:39:14 +0000</pubDate>
      <link>https://dev.to/justjinoit/removing-api-keys-from-git-history-bfg-force-push-a-security-incident-response-3obo</link>
      <guid>https://dev.to/justjinoit/removing-api-keys-from-git-history-bfg-force-push-a-security-incident-response-3obo</guid>
      <description>&lt;p&gt;&lt;strong&gt;Published on&lt;/strong&gt;: 2026-06-06&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time&lt;/strong&gt;: 5 min&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Tags&lt;/strong&gt;: #security #git #devops #secrets&lt;/p&gt;
&lt;h2&gt;
  
  
  The Situation
&lt;/h2&gt;

&lt;p&gt;API keys were committed to &lt;code&gt;.env&lt;/code&gt; file in Git history. Anyone with repo access could do &lt;code&gt;git log&lt;/code&gt; and see them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--full-history&lt;/span&gt; &lt;span class="nt"&gt;--name-only&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt;
&lt;span class="c"&gt;# abc1234 Remove .env from version control&lt;/span&gt;
&lt;span class="c"&gt;# def5678 Initial commit: Contest Agent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Immediate risk&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clone repo → check git history → steal API keys&lt;/li&gt;
&lt;li&gt;If pushed to GitHub, keys are potentially searchable/cached&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solution: BFG Repo-Cleaner
&lt;/h2&gt;

&lt;p&gt;Better than &lt;code&gt;git filter-branch&lt;/code&gt; (faster, safer):&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Install BFG
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;bfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Delete from History
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;your-project
bfg &lt;span class="nt"&gt;--delete-files&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="nt"&gt;--no-blob-protection&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deleted files:
  Deleted 4 commits
  Deleted 12 blob(s)
  ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Clean Up
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reflog expire &lt;span class="nt"&gt;--expire&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;now &lt;span class="nt"&gt;--all&lt;/span&gt;
git gc &lt;span class="nt"&gt;--prune&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;now &lt;span class="nt"&gt;--aggressive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Verify
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--full-history&lt;/span&gt; &lt;span class="nt"&gt;--name-only&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt;
&lt;span class="c"&gt;# (no output = success)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Force Push
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push origin main &lt;span class="nt"&gt;--force-with-lease&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Before &amp;amp; After
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before BFG&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt;
&lt;span class="go"&gt;abc1234 Remove .env from version control
def5678 Enable SSL certificate verification
ghi9012 Standardize dependencies
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After BFG&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt;
&lt;span class="go"&gt;xyz3456 Remove duplicate service definition
uvw7890 Standardize dependencies
rst1234 Enable SSL certificate verification
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unnecessary commits removed, history cleaned.&lt;/p&gt;

&lt;h2&gt;
  
  
  BFG vs git filter-branch
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;git filter-branch&lt;/th&gt;
&lt;th&gt;BFG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Speed&lt;/td&gt;
&lt;td&gt;Slow (1000+ commits)&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safety&lt;/td&gt;
&lt;td&gt;Complex, error-prone&lt;/td&gt;
&lt;td&gt;Simple, clear feedback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Usability&lt;/td&gt;
&lt;td&gt;Requires scripting&lt;/td&gt;
&lt;td&gt;One-liner&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recommendation&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  CRITICAL: Rotate API Keys
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cleaning git history is step 1.&lt;/strong&gt; Step 2 is mandatory:&lt;/p&gt;

&lt;h3&gt;
  
  
  Timeline: Do This Immediately
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Within 1 hour&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Anthropic&lt;/span&gt;
&lt;span class="c"&gt;# → https://console.anthropic.com/account/keys&lt;/span&gt;
&lt;span class="c"&gt;# → Delete old key, generate new one&lt;/span&gt;

&lt;span class="c"&gt;# Supabase&lt;/span&gt;
&lt;span class="c"&gt;# → https://app.supabase.com → Settings → API&lt;/span&gt;
&lt;span class="c"&gt;# → Click "Reset secret key"&lt;/span&gt;

&lt;span class="c"&gt;# Telegram&lt;/span&gt;
&lt;span class="c"&gt;# → Open @BotFather&lt;/span&gt;
&lt;span class="c"&gt;# → /token → select bot → /revoke&lt;/span&gt;
&lt;span class="c"&gt;# → Generate new token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Within 24 hours&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update all deployed servers with new keys&lt;/li&gt;
&lt;li&gt;Test that services still work&lt;/li&gt;
&lt;li&gt;Monitor logs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Best Practices Going Forward
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Initialize Correctly from Day One
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# First thing in new project:&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore
git add .gitignore
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add .env to .gitignore"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Create &lt;code&gt;.env.example&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# .env.example
&lt;/span&gt;&lt;span class="py"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-key-here&lt;/span&gt;
&lt;span class="py"&gt;SUPABASE_URL&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-url-here&lt;/span&gt;
&lt;span class="py"&gt;TELEGRAM_BOT_TOKEN&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-token-here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Use GitHub Actions Secrets
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/deploy.yml&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ANTHROPIC_API_KEY }}&lt;/span&gt;
  &lt;span class="na"&gt;SUPABASE_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SUPABASE_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Add Pre-Commit Hook (Optional)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# .git/hooks/pre-commit&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;git diff &lt;span class="nt"&gt;--cached&lt;/span&gt; &lt;span class="nt"&gt;--name-only&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'\.env'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ Error: Attempting to commit .env file"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real Impact (After Remediation)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before:
- .env in git history
- 79 commits total
- History size: 50MB

After BFG:
- .env removed from all commits
- 75 commits total (4 cleaned)
- History size: 42MB (-16%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Execution Checklist
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Phase 1: Immediate (Next 1 hour)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Run BFG: &lt;code&gt;bfg --delete-files ".env" .&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Clean git: &lt;code&gt;git reflog expire ... &amp;amp;&amp;amp; git gc ...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Force push: &lt;code&gt;git push origin main --force-with-lease&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Phase 2: Rotate (Next 24 hours)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Anthropic API key&lt;/li&gt;
&lt;li&gt;[ ] Supabase Secret Key&lt;/li&gt;
&lt;li&gt;[ ] Telegram Bot token&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Phase 3: Prevention&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Update deployed servers with new keys&lt;/li&gt;
&lt;li&gt;[ ] Create &lt;code&gt;.env.example&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Update &lt;code&gt;.gitignore&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Add GitHub Actions secrets&lt;/li&gt;
&lt;li&gt;[ ] Notify team (force push happened)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Phase 4: Future&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Set up pre-commit hooks&lt;/li&gt;
&lt;li&gt;[ ] Monthly secret audit&lt;/li&gt;
&lt;li&gt;[ ] Use Secret scanning tools (GitHub, GitLab)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Takeaway
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;API key exposure is a security incident, not a "mistake."&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Act fast (BFG)&lt;/li&gt;
&lt;li&gt;Rotate keys (mandatory)&lt;/li&gt;
&lt;li&gt;Document the response&lt;/li&gt;
&lt;li&gt;Prevent recurrence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your security posture will be better for it—and you'll have a good story for the next security audit.&lt;/p&gt;

</description>
      <category>security</category>
      <category>git</category>
      <category>devops</category>
      <category>secrets</category>
    </item>
    <item>
      <title>API 키 Git 히스토리에서 완전 제거: BFG + force push 가이드</title>
      <dc:creator>JustJinoIT</dc:creator>
      <pubDate>Sat, 06 Jun 2026 10:39:13 +0000</pubDate>
      <link>https://dev.to/justjinoit/api-ki-git-hiseutorieseo-wanjeon-jegeo-bfg-force-push-gaideu-1pf0</link>
      <guid>https://dev.to/justjinoit/api-ki-git-hiseutorieseo-wanjeon-jegeo-bfg-force-push-gaideu-1pf0</guid>
      <description>&lt;p&gt;&lt;strong&gt;Published on&lt;/strong&gt;: 2026-06-06&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time&lt;/strong&gt;: 5 min&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Tags&lt;/strong&gt;: #security #git #devops #secrets&lt;/p&gt;
&lt;h2&gt;
  
  
  상황
&lt;/h2&gt;

&lt;p&gt;API 키가 &lt;code&gt;.env&lt;/code&gt; 파일로 Git 히스토리에 커밋되었습니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--full-history&lt;/span&gt; &lt;span class="nt"&gt;--name-only&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt;
&lt;span class="c"&gt;# f300032 Remove .env from version control (security: API keys exposed)&lt;/span&gt;
&lt;span class="c"&gt;# c9de179 Initial commit: Contest Agent - AI-powered contest automation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;즉각적인 위험&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;누구든 git clone → git log → 과거 커밋에서 API 키 확인 가능&lt;/li&gt;
&lt;li&gt;GitHub에 push된 경우 archive/search에서도 접근 가능&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  해결책: BFG Repo-Cleaner
&lt;/h2&gt;

&lt;p&gt;git filter-branch보다 빠르고 안전합니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. BFG 설치
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;bfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. 히스토리에서 .env 삭제
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;your-project
bfg &lt;span class="nt"&gt;--delete-files&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="nt"&gt;--no-blob-protection&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;출력&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deleted files

  Deleted 4 commits
  Deleted 12 blob(s)

Cleanup:

  WARNING: The 'pack' folder will be deleted...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Git 정리
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reflog expire &lt;span class="nt"&gt;--expire&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;now &lt;span class="nt"&gt;--all&lt;/span&gt;
git gc &lt;span class="nt"&gt;--prune&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;now &lt;span class="nt"&gt;--aggressive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Verify
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--full-history&lt;/span&gt; &lt;span class="nt"&gt;--name-only&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt;
&lt;span class="c"&gt;# (출력 없음 = 성공)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Force Push
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push origin main &lt;span class="nt"&gt;--force-with-lease&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  단계별 확인
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt;
&lt;span class="go"&gt;abc1234 Remove .env from version control
def5678 Enable SSL certificate verification
ghi9012 Standardize dependencies
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt;
&lt;span class="go"&gt;xyz3456 Remove duplicate service definition
uvw7890 Standardize dependencies
rst1234 Enable SSL certificate verification
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ 불필요한 커밋들이 정리되고, 히스토리 크기도 감소&lt;/p&gt;

&lt;h2&gt;
  
  
  git filter-branch vs BFG
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;기준&lt;/th&gt;
&lt;th&gt;git filter-branch&lt;/th&gt;
&lt;th&gt;BFG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;속도&lt;/td&gt;
&lt;td&gt;느림 (1000+ 커밋)&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;td&gt;간단함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;명확성&lt;/td&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;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  API 키 로테이션 (필수!)
&lt;/h2&gt;

&lt;p&gt;히스토리 정리 후에도 &lt;strong&gt;API 키 반드시 교체&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Anthropic&lt;/span&gt;
&lt;span class="c"&gt;# https://console.anthropic.com/account/keys&lt;/span&gt;
&lt;span class="c"&gt;# → 기존 키 삭제 → 새 키 생성&lt;/span&gt;

&lt;span class="c"&gt;# 2. Supabase&lt;/span&gt;
&lt;span class="c"&gt;# https://app.supabase.com → Settings → API&lt;/span&gt;
&lt;span class="c"&gt;# → "Reset secret key" 클릭&lt;/span&gt;

&lt;span class="c"&gt;# 3. Telegram&lt;/span&gt;
&lt;span class="c"&gt;# @BotFather → /token → 봇 선택 → /revoke&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  모범 사례
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. .env 초기화
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .gitignore에 추가 (처음부터)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore
git add .gitignore
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add .env to .gitignore"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. .env.example 제공
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# .env.example
&lt;/span&gt;&lt;span class="py"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-key-here&lt;/span&gt;
&lt;span class="py"&gt;SUPABASE_URL&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-url-here&lt;/span&gt;
&lt;span class="py"&gt;TELEGRAM_BOT_TOKEN&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-token-here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. CI/CD에서 시크릿 관리
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# GitHub Actions&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ANTHROPIC_API_KEY }}&lt;/span&gt;
  &lt;span class="na"&gt;SUPABASE_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SUPABASE_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  실제 적용 결과
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before:
  - Git history에 키 노출
  - 총 79개 커밋
  - 히스토리 크기: 50MB

After BFG:
  - .env 완전 제거
  - 총 75개 커밋 (4개 정리)
  - 히스토리 크기: 42MB (-16%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  실행 체크리스트
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. 즉시 (사고 대응)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] BFG로 히스토리 정리: &lt;code&gt;bfg --delete-files ".env" .&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] 로컬 정리: &lt;code&gt;git reflog expire --expire=now --all &amp;amp;&amp;amp; git gc --aggressive&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] 강제 푸시: &lt;code&gt;git push origin main --force-with-lease&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. 1시간 내 (키 교체)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Anthropic API 키 로테이션&lt;/li&gt;
&lt;li&gt;[ ] Supabase Secret Key 리셋&lt;/li&gt;
&lt;li&gt;[ ] Telegram Bot 토큰 revoke + 재발급&lt;/li&gt;
&lt;li&gt;[ ] 배포 서버 환경변수 업데이트&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. 당일 (예방)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] .env.example 생성 (템플릿)&lt;/li&gt;
&lt;li&gt;[ ] .gitignore에 .env 명시&lt;/li&gt;
&lt;li&gt;[ ] 팀원/스카우터에 알림&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. 향후 (재발 방지)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] GitHub Actions secrets 사용&lt;/li&gt;
&lt;li&gt;[ ] pre-commit hook으로 .env 자동 차단&lt;/li&gt;
&lt;li&gt;[ ] 월간 보안 감시&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  결론
&lt;/h2&gt;

&lt;p&gt;API 키 노출은 &lt;strong&gt;보안 사고&lt;/strong&gt;입니다. BFG로 빠르게 정리하고, 즉시 키를 교체하세요.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;앞으로&lt;/strong&gt;: &lt;code&gt;.env&lt;/code&gt;는 첫 커밋부터 &lt;code&gt;.gitignore&lt;/code&gt;에 넣기. 가장 간단한 방지책입니다.&lt;/p&gt;

</description>
      <category>security</category>
      <category>git</category>
      <category>devops</category>
      <category>secrets</category>
    </item>
    <item>
      <title>Multi-Cloud Deployment in Production: Cloud Run, Railway, Oracle Cloud (3-Month Report)</title>
      <dc:creator>JustJinoIT</dc:creator>
      <pubDate>Sat, 06 Jun 2026 10:28:09 +0000</pubDate>
      <link>https://dev.to/justjinoit/multi-cloud-deployment-in-production-cloud-run-railway-oracle-cloud-3-month-report-53po</link>
      <guid>https://dev.to/justjinoit/multi-cloud-deployment-in-production-cloud-run-railway-oracle-cloud-3-month-report-53po</guid>
      <description>&lt;p&gt;&lt;strong&gt;Published on&lt;/strong&gt;: 2026-06-06&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time&lt;/strong&gt;: 10 min&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Tags&lt;/strong&gt;: #devops #cloud #fastapi #production&lt;/p&gt;
&lt;h2&gt;
  
  
  Situation
&lt;/h2&gt;

&lt;p&gt;I deployed 3 FastAPI projects to 3 different clouds. Here's &lt;strong&gt;what actually happened&lt;/strong&gt; (not marketing speak):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contest-agent      → Google Cloud Run
ai-insight-curator → Railway
ai-lifelogger      → Oracle Cloud Always Free
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  1. Google Cloud Run: 20+ Deployments Before Discovering the Real Problem
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue: "Container Failed to Start"
&lt;/h3&gt;

&lt;p&gt;Deployed 20+ times, same error every time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build: SUCCESS ✅
Push: SUCCESS ✅
Start: TIMEOUT ❌
Port 8080 binding: TIMEOUT ❌
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root cause&lt;/strong&gt;: FastAPI startup was blocking port binding with I/O operations&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ Problem code (startup blocks port binding)
&lt;/span&gt;&lt;span class="nd"&gt;@asynccontextmanager&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;telegram_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Starting...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# I/O blocking
&lt;/span&gt;    &lt;span class="n"&gt;db_check&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test_connection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;              &lt;span class="c1"&gt;# I/O blocking  
&lt;/span&gt;    &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                                   &lt;span class="c1"&gt;# Heavy init
&lt;/span&gt;    &lt;span class="k"&gt;yield&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cloud Run waits for port binding to complete before health checks. Startup blocking = timeout.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution: Lazy Loading
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ✅ Fixed code (startup returns immediately)
&lt;/span&gt;&lt;span class="n"&gt;_initialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lazy_init&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;_initialized&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_initialized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="n"&gt;_initialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;telegram_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Started&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/webhook&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;lazy_init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Init on first actual request
&lt;/span&gt;    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: Startup 100ms (was 60s+ timeout), port binding immediate, health check passes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Lesson: Start Minimal
&lt;/h3&gt;

&lt;p&gt;Don't deploy a complex system all at once. Lessons learned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Phase 1: Just "/" endpoint
&lt;/span&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;# → Deploy, test, pass ✅
&lt;/span&gt;
&lt;span class="c1"&gt;# Phase 2: Add health check
&lt;/span&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/health&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;health&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;healthy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;# → Deploy, test, pass ✅
&lt;/span&gt;
&lt;span class="c1"&gt;# Phase 3-N: Gradually add features
# Each phase = one deployment test
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Railway: The "Simple" Illusion
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Git push → auto-deploy (very fast)&lt;/li&gt;
&lt;li&gt;PostgreSQL, Redis built-in&lt;/li&gt;
&lt;li&gt;Intuitive dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Reality Check
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cost surprises&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected: $10/month
Actual: $25/month (250% overage)

Reason:
- 1 vCPU + 512MB RAM always running
- No cold start = memory always consumed
- Bandwidth costs added up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Memory leak detection is hard&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hour 1: 150MB ✅
Hour 2: 180MB
Hour 3: 220MB
Hour 4: 260MB (OOM incoming)

Cause: RSS feed crawler not releasing memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Auto-deploy is a double-edged sword&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Con: Changes go live without testing&lt;/li&gt;
&lt;li&gt;Con: Need fast rollback procedure&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How I Actually Operate It
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Before pushing to main:&lt;/span&gt;
pytest              &lt;span class="c"&gt;# Run tests&lt;/span&gt;
pylint             &lt;span class="c"&gt;# Lint check&lt;/span&gt;
docker build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker run  &lt;span class="c"&gt;# Local test&lt;/span&gt;

&lt;span class="c"&gt;# Only push after passing:&lt;/span&gt;
git push origin main  &lt;span class="c"&gt;# Auto-deploys&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Oracle Cloud Always Free: Free but Demanding
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Completely free (4 CPU, 24GB RAM, 200GB storage)&lt;/li&gt;
&lt;li&gt;No limits&lt;/li&gt;
&lt;li&gt;Full SSH control&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Real Problems
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem #1: 1GB instance, pip install fails&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MemoryError during pip install

Reason: 1GB RAM instance can't handle 
all packages at once
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add swap&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;fallocate &lt;span class="nt"&gt;-l&lt;/span&gt; 8G /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;mkswap /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;swapon /swapfile

&lt;span class="c"&gt;# Or: Install only essentials&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; anthropic supabase python-telegram-bot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problem #2: Docker vs Local Mismatch&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local: anthropic==0.40.0 (already installed)
Docker: Fresh install reads requirements.txt
  - anthropic==0.40.0
  - langchain-anthropic needs anthropic&amp;gt;=0.41.0
  → pip can't resolve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Remove version pins, let pip resolve&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DON'T: anthropic==0.40.0, supabase==2.0.0, ...
DO: anthropic, supabase (let pip figure it out)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problem #3: SSH Deployment Needs Automation&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Manual (every time):&lt;/span&gt;
ssh oracle@your-ip
&lt;span class="nb"&gt;cd&lt;/span&gt; /opt/ai-lifelogger
git pull &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; systemctl restart

&lt;span class="c"&gt;# Better (automated via GitHub Actions):&lt;/span&gt;
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt; oracle@&lt;span class="nv"&gt;$ip&lt;/span&gt; &lt;span class="s2"&gt;"cd /opt &amp;amp;&amp;amp; git pull &amp;amp;&amp;amp; systemctl restart"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance Comparison (3-Month Data)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Cloud Run&lt;/th&gt;
&lt;th&gt;Railway&lt;/th&gt;
&lt;th&gt;Oracle&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deploy time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2-3 min&lt;/td&gt;
&lt;td&gt;30 sec&lt;/td&gt;
&lt;td&gt;5 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cold start&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3-5 sec&lt;/td&gt;
&lt;td&gt;0 sec&lt;/td&gt;
&lt;td&gt;&amp;lt;1 sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Monthly cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$15&lt;/td&gt;
&lt;td&gt;$25&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CPU limit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2 cores&lt;/td&gt;
&lt;td&gt;1 core&lt;/td&gt;
&lt;td&gt;4 cores&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RAM limit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2GB&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;24GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Solid&lt;/td&gt;
&lt;td&gt;⚠️ Memory issues&lt;/td&gt;
&lt;td&gt;✅ Solid&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Practical Advice
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Start Minimal, Add Gradually
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Deploy "/" endpoint first&lt;/li&gt;
&lt;li&gt;Test, pass, add next feature&lt;/li&gt;
&lt;li&gt;Repeat&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Always Test Locally
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Choose Based on Use Case
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High traffic&lt;/strong&gt;: Cloud Run (autoscales)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Medium traffic&lt;/strong&gt;: Railway (simple)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low traffic&lt;/strong&gt;: Oracle (free)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Monitoring is Non-Negotiable
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cloud Run: GCP Logs + Cloud Monitoring
Railway: Built-in dashboard (limited)
Oracle: SSH → journalctl + tail -f
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;There's no "perfect" platform.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Run: startup timeout (solvable with lazy loading)&lt;/li&gt;
&lt;li&gt;Railway: memory leaks (code issue, not platform)&lt;/li&gt;
&lt;li&gt;Oracle: operational overhead (worth it for free tier)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The real skill&lt;/strong&gt;: Understanding each platform's constraints and designing around them.&lt;/p&gt;

&lt;p&gt;The 20+ Cloud Run deployment failures? They taught me more than 10 successful deployments would have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom Line
&lt;/h2&gt;

&lt;p&gt;If you're deploying to multiple clouds, expect problems—but they're solvable. Document them, learn from them, share them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your experience matters to other developers.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cloud</category>
      <category>fastapi</category>
      <category>production</category>
    </item>
    <item>
      <title>실전 다중 클라우드 배포: Cloud Run, Railway, Oracle Cloud 3개월 운영 기록</title>
      <dc:creator>JustJinoIT</dc:creator>
      <pubDate>Sat, 06 Jun 2026 10:28:08 +0000</pubDate>
      <link>https://dev.to/justjinoit/siljeon-dajung-keulraudeu-baepo-cloud-run-railway-oracle-cloud-3gaeweol-unyeong-girog-1j02</link>
      <guid>https://dev.to/justjinoit/siljeon-dajung-keulraudeu-baepo-cloud-run-railway-oracle-cloud-3gaeweol-unyeong-girog-1j02</guid>
      <description>&lt;p&gt;&lt;strong&gt;Published on&lt;/strong&gt;: 2026-06-06&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time&lt;/strong&gt;: 10 min&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Tags&lt;/strong&gt;: #devops #cloud #fastapi #production&lt;/p&gt;
&lt;h2&gt;
  
  
  상황
&lt;/h2&gt;

&lt;p&gt;3개의 FastAPI 프로젝트를 3개의 다른 클라우드에 배포했습니다. 각 플랫폼의 &lt;strong&gt;실제 문제&lt;/strong&gt;와 해결 방법을 공유합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;contest&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;      → &lt;span class="n"&gt;Google&lt;/span&gt; &lt;span class="n"&gt;Cloud&lt;/span&gt; &lt;span class="n"&gt;Run&lt;/span&gt;
&lt;span class="n"&gt;ai&lt;/span&gt;-&lt;span class="n"&gt;insight&lt;/span&gt;-&lt;span class="n"&gt;curator&lt;/span&gt; → &lt;span class="n"&gt;Railway&lt;/span&gt;  
&lt;span class="n"&gt;ai&lt;/span&gt;-&lt;span class="n"&gt;lifelogger&lt;/span&gt;      → &lt;span class="n"&gt;Oracle&lt;/span&gt; &lt;span class="n"&gt;Cloud&lt;/span&gt; &lt;span class="n"&gt;Always&lt;/span&gt; &lt;span class="n"&gt;Free&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  1. Google Cloud Run: 20+ 배포 시도 끝에 발견한 Startup Timeout
&lt;/h2&gt;

&lt;h3&gt;
  
  
  문제: "The container failed to start and listen on port 8080"
&lt;/h3&gt;

&lt;p&gt;20번 이상 배포 시도했지만 계속 실패했습니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deployment logs:
- Build: SUCCESS ✅
- Push to Registry: SUCCESS ✅
- Container Start: TIMEOUT ❌
- Port 8080 binding: TIMEOUT ❌
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;원인&lt;/strong&gt;: FastAPI startup 단계에서 I/O 작업이 port binding을 블로킹&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ 문제 코드 (startup 중 I/O blocking)
&lt;/span&gt;&lt;span class="nd"&gt;@asynccontextmanager&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;telegram_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Starting...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# I/O 대기
&lt;/span&gt;    &lt;span class="n"&gt;db_check&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test_connection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;              &lt;span class="c1"&gt;# I/O 대기
&lt;/span&gt;    &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                                   &lt;span class="c1"&gt;# 무거운 초기화
&lt;/span&gt;    &lt;span class="k"&gt;yield&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cloud Run은 port binding 완료 후 health check를 하는데, startup이 block되면서 timeout 발생.&lt;/p&gt;

&lt;h3&gt;
  
  
  해결책: Lazy Loading
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ✅ 해결 코드 (startup 최소화)
&lt;/span&gt;&lt;span class="n"&gt;_initialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lazy_init&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;_initialized&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_initialized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="n"&gt;_initialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="c1"&gt;# 무거운 작업은 첫 요청 시점으로 이동
&lt;/span&gt;    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;telegram_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Started&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/webhook&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;lazy_init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# 첫 요청에서 초기화
&lt;/span&gt;    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;효과&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Startup: 100ms (이전 60초+ timeout)&lt;/li&gt;
&lt;li&gt;Port binding: 즉시 성공&lt;/li&gt;
&lt;li&gt;Health check: 통과&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  교훈: 극-최소 버전부터 시작
&lt;/h3&gt;

&lt;p&gt;복합 시스템을 한번에 올리면 원인 파악이 어렵습니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 1단계: "/" endpoint만
&lt;/span&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# 배포 테스트 → 성공 ✅
&lt;/span&gt;
&lt;span class="c1"&gt;# 2단계: health check 추가
&lt;/span&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/health&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;health&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;healthy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# 배포 테스트 → 성공 ✅
&lt;/span&gt;
&lt;span class="c1"&gt;# 3단계: webhook 추가
# ... 계속 점진적 추가
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Railway: "간단"하다는 착각
&lt;/h2&gt;

&lt;h3&gt;
  
  
  장점
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Git push → 자동 배포 (매우 빠름)&lt;/li&gt;
&lt;li&gt;PostgreSQL, Redis 통합 쉬움&lt;/li&gt;
&lt;li&gt;대시보드 직관적&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  실제 문제들
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1) 비용 계산 불가&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;예상: $10/월
실제: $25/월 (예상 250% 초과)

원인:
- 1vCPU + 512MB RAM 지속 실행
- 항상 ON (cold start 없음 = 메모리 지속 소비)
- Bandwidth 추가 비용
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2) Memory leak 감지 어려움&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Railway는 프로세스별 메모리 모니터링이 제한적입니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;메모리 사용량:
시간 1: 150MB ✅
시간 2: 180MB
시간 3: 220MB
시간 4: 260MB (OOM 위험)

원인: RSS 크롤링 중 메모리 해제 누락
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3) 자동 재배포의 양날검&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Git push → 자동 배포는 편하지만:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;테스트 없이 배포되는 위험&lt;/li&gt;
&lt;li&gt;문제 발생 시 빠른 롤백 필요&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  실제 운영 방식
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# GitHub에 push하기 전에&lt;/span&gt;
pytest  &lt;span class="c"&gt;# 테스트&lt;/span&gt;
pylint  &lt;span class="c"&gt;# 린트&lt;/span&gt;
docker build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker run  &lt;span class="c"&gt;# 로컬 테스트&lt;/span&gt;

&lt;span class="c"&gt;# 통과 후 push (자동 배포)&lt;/span&gt;
git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Oracle Cloud Always Free: 무료지만 운영 수고
&lt;/h2&gt;

&lt;h3&gt;
  
  
  장점
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;완전 무료 (4 CPU, 24GB RAM, 200GB storage)&lt;/li&gt;
&lt;li&gt;제약 없음&lt;/li&gt;
&lt;li&gt;SSH 전체 제어&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  실제 문제들
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1) 1GB RAM 서버에서 pip install 실패&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install 중:
MemoryError: Unable to allocate 500MB

원인: 1GB 인스턴스인데 모든 패키지를 한번에 설치
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;해결책&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# swap 추가&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;fallocate &lt;span class="nt"&gt;-l&lt;/span&gt; 8G /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;mkswap /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;swapon /swapfile

&lt;span class="c"&gt;# 또는 필수 패키지만 설치&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; anthropic supabase python-telegram-bot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2) 버전 호환성 (Docker vs 로컬)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;로컬에서: anthropic==0.40.0 (이미 설치됨)
Docker에서: requirements.txt 처음부터 해석
  - anthropic==0.40.0
  - langchain-anthropic이 요구: anthropic&amp;gt;=0.41.0
  → pip 해석 불가능
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;해결책&lt;/strong&gt;: 버전 고정 제거, pip 자동 해결&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DO NOT:  anthropic==0.40.0, supabase==2.0.0, ...
DO THIS: anthropic, supabase (pip resolve)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3) SSH 배포 자동화 필요&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 수동 배포 (매번 SSH 접속)&lt;/span&gt;
ssh oracle@your-ip
&lt;span class="nb"&gt;cd&lt;/span&gt; /opt/ai-lifelogger
git pull
systemctl restart

&lt;span class="c"&gt;# 자동화 (GitHub Actions)&lt;/span&gt;
- name: Deploy to Oracle
  run: |
    ssh &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;{ secrets.ORACLE_KEY &lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; oracle@&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;{ secrets.ORACLE_IP &lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="s2"&gt;"cd /opt &amp;amp;&amp;amp; git pull &amp;amp;&amp;amp; systemctl restart"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  성능 비교 (3개월 운영 데이터)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;지표&lt;/th&gt;
&lt;th&gt;Cloud Run&lt;/th&gt;
&lt;th&gt;Railway&lt;/th&gt;
&lt;th&gt;Oracle&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;배포 시간&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2-3분&lt;/td&gt;
&lt;td&gt;30초&lt;/td&gt;
&lt;td&gt;5분&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cold start&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3-5초&lt;/td&gt;
&lt;td&gt;0초&lt;/td&gt;
&lt;td&gt;&amp;lt;1초&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;월 비용&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$15&lt;/td&gt;
&lt;td&gt;$25&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CPU 제한&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2개&lt;/td&gt;
&lt;td&gt;1개&lt;/td&gt;
&lt;td&gt;4개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;메모리 제한&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2GB&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;24GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;상태&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;안정적 ✅&lt;/td&gt;
&lt;td&gt;메모리 leak&lt;/td&gt;
&lt;td&gt;안정적 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  실전 팁
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. 극-최소 버전부터 시작
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;"모든 게 한번에 되겠지"는 환상&lt;/li&gt;
&lt;li&gt;각 단계마다 배포 검증&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. 로컬 테스트는 필수
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. 비용과 성능의 트레이드오프
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;높은 트래픽&lt;/strong&gt;: Cloud Run (자동 스케일)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;중간 규모&lt;/strong&gt;: Railway (간편함 but 비용)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;낮은 트래픽&lt;/strong&gt;: Oracle (무료 but 관리 수고)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. 모니터링은 선택이 아닌 필수
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cloud Run: GCP Logs + Cloud Monitoring
Railway: 내장 대시보드 (제한적)
Oracle: SSH → journalctl + tail -f
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  결론
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;각 플랫폼이 "완벽"한 경우는 없습니다.&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Run: startup timeout (해결 가능)&lt;/li&gt;
&lt;li&gt;Railway: 비용 + 메모리 (환경 의존)&lt;/li&gt;
&lt;li&gt;Oracle: 운영 수고 (자동화로 해결)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;중요한 건 문제를 인식하고, 체계적으로 대응하는 것입니다.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;특히 startup timeout처럼 "아, 그거구나"하면 해결되는 문제도 있고, 메모리 leak처럼 근본 코드 개선이 필요한 문제도 있습니다.&lt;/p&gt;

&lt;p&gt;3개 클라우드를 다루면서 배운 가장 큰 교훈: &lt;strong&gt;극-최소 버전부터 시작하세요.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cloud</category>
      <category>fastapi</category>
      <category>production</category>
    </item>
    <item>
      <title>From ThreadPoolExecutor to httpx AsyncClient: True Async Refactoring</title>
      <dc:creator>JustJinoIT</dc:creator>
      <pubDate>Sat, 06 Jun 2026 09:46:17 +0000</pubDate>
      <link>https://dev.to/justjinoit/from-threadpoolexecutor-to-httpx-asyncclient-true-async-refactoring-5909</link>
      <guid>https://dev.to/justjinoit/from-threadpoolexecutor-to-httpx-asyncclient-true-async-refactoring-5909</guid>
      <description>&lt;p&gt;&lt;strong&gt;Published on&lt;/strong&gt;: 2026-06-06&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time&lt;/strong&gt;: 6 min&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Tags&lt;/strong&gt;: #python #async #performance #optimization&lt;/p&gt;
&lt;h2&gt;
  
  
  The Problem: Fake Async
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;supabase-async&lt;/code&gt; library claimed to be async but actually wrapped synchronous calls with ThreadPoolExecutor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ Fake async (old code)
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SupabaseAsync&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_event_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_in_executor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_executor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Sync call wrapped as async
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problems&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Max 3 concurrent requests (not scalable)&lt;/li&gt;
&lt;li&gt;Thread overhead per request&lt;/li&gt;
&lt;li&gt;High memory usage&lt;/li&gt;
&lt;li&gt;No connection pooling&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Solution: httpx AsyncClient
&lt;/h2&gt;

&lt;p&gt;Use true async HTTP with httpx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ✅ Real async (new code)
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SupabaseAsync&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;limits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Limits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_connections&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_get_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance Gains
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;ThreadPoolExecutor(3)&lt;/th&gt;
&lt;th&gt;httpx(10)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Max concurrent&lt;/td&gt;
&lt;td&gt;3 requests&lt;/td&gt;
&lt;td&gt;10 requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avg response&lt;/td&gt;
&lt;td&gt;450ms&lt;/td&gt;
&lt;td&gt;150ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory usage&lt;/td&gt;
&lt;td&gt;250MB&lt;/td&gt;
&lt;td&gt;180MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Throughput&lt;/td&gt;
&lt;td&gt;6.7 req/s&lt;/td&gt;
&lt;td&gt;20 req/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Real benchmark&lt;/strong&gt;: 100 concurrent requests&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ThreadPoolExecutor: 15 seconds&lt;/li&gt;
&lt;li&gt;httpx AsyncClient: 5 seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3x faster&lt;/strong&gt; ⚡&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Migration Steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Client Initialization with Lazy Loading
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;limits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Limits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;max_connections&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;max_keepalive_connections&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. HTTP Methods (GET, POST, etc.)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_get_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# ... more methods
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Context Manager Support
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aclose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__aenter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__aexit__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_tb&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Usage
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;SupabaseAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contests&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Production Results
&lt;/h2&gt;

&lt;p&gt;After deploying to contest-agent (FastAPI on Cloud Run):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Response time: 450ms → 150ms (3x faster)
Memory: 250MB → 180MB (28% reduction)
Concurrent capacity: 3 → 10 (3.3x increase)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Differences
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;ThreadPoolExecutor&lt;/th&gt;
&lt;th&gt;httpx&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Type&lt;/td&gt;
&lt;td&gt;Thread pool + sync I/O&lt;/td&gt;
&lt;td&gt;True async I/O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concurrency&lt;/td&gt;
&lt;td&gt;Limited by thread count&lt;/td&gt;
&lt;td&gt;Limited by system resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;Thread overhead per request&lt;/td&gt;
&lt;td&gt;Minimal overhead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connection pooling&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Automatic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning curve&lt;/td&gt;
&lt;td&gt;Easy (familiar pattern)&lt;/td&gt;
&lt;td&gt;Moderate (async patterns)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Migration Cautions
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Exception types change&lt;/strong&gt;: &lt;code&gt;requests.HTTPError&lt;/code&gt; → &lt;code&gt;httpx.HTTPError&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection pooling is automatic&lt;/strong&gt;: Don't manually manage connections&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeout behavior&lt;/strong&gt;: Slightly different, but compatible&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Lessons
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ThreadPoolExecutor is a band-aid.&lt;/strong&gt; For truly async I/O:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use real async HTTP clients (httpx, aiohttp)&lt;/li&gt;
&lt;li&gt;Understand async/await patterns&lt;/li&gt;
&lt;li&gt;Design from async-first perspective&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is especially critical for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High-concurrency services (web crawlers, API gateways)&lt;/li&gt;
&lt;li&gt;Limited resources (serverless, microservices)&lt;/li&gt;
&lt;li&gt;Real-time applications&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Going from fake async to true async isn't just a performance win—it's a design improvement. You get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3x faster response times&lt;/li&gt;
&lt;li&gt;30% memory savings&lt;/li&gt;
&lt;li&gt;Unlimited concurrency (within reason)&lt;/li&gt;
&lt;li&gt;Proper resource management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your async code uses &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; or &lt;code&gt;loop.run_in_executor&lt;/code&gt;, &lt;strong&gt;refactor it now.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>async</category>
      <category>performance</category>
      <category>optimization</category>
    </item>
    <item>
      <title>Security Audit of 6 Python Projects: 25 Issues Found &amp; Fixed</title>
      <dc:creator>JustJinoIT</dc:creator>
      <pubDate>Sat, 06 Jun 2026 09:46:16 +0000</pubDate>
      <link>https://dev.to/justjinoit/security-audit-of-6-python-projects-25-issues-found-fixed-329</link>
      <guid>https://dev.to/justjinoit/security-audit-of-6-python-projects-25-issues-found-fixed-329</guid>
      <description>&lt;p&gt;&lt;strong&gt;Published on&lt;/strong&gt;: 2026-06-06&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time&lt;/strong&gt;: 8 min&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Tags&lt;/strong&gt;: #security #python #audit #devops&lt;/p&gt;
&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Over 3 months, I developed and audited 6 Python projects (3 bots + 3 libraries): a FastAPI + Telegram Bot + LLM integration system. I discovered 25 security/code issues and fixed 23 immediately.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Audit scope&lt;/strong&gt;: 91 Python files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issues found&lt;/strong&gt;: 25 (5 critical, 18 medium, 2 minor)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix rate&lt;/strong&gt;: 92% (23/25)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Critical Issues - 5
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. API Keys Exposed in Git History 🔴
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Anthropic, Supabase, and Telegram API keys committed in &lt;code&gt;.env&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ Exposed (visible in git log)
&lt;/span&gt;&lt;span class="n"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ant&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;api03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;xxxxxxxxxx&lt;/span&gt;
&lt;span class="n"&gt;SUPABASE_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sb_publishable_xxxxxxxxxx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Risk&lt;/strong&gt;: Anyone can access previous commits and steal API keys → resource abuse, data breach&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Clean history with BFG&lt;/span&gt;
bfg &lt;span class="nt"&gt;--delete-files&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="nt"&gt;--no-blob-protection&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# 2. Remove from Git&lt;/span&gt;
git &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;--cached&lt;/span&gt; .env
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore

&lt;span class="c"&gt;# 3. Rotate API keys (mandatory)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. SSL Verification Disabled (MITM Attack Risk) 🔴
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: &lt;code&gt;verify=False&lt;/code&gt; used in 10 places&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ Insecure
&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ✅ Secure
&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# default
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: HTTPS man-in-the-middle attacks possible → sensitive data exposed&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Overly Broad Exception Handling 🔴
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: &lt;code&gt;except Exception&lt;/code&gt; silencing all errors (114 instances)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ No error tracking
&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;db_select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contests&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# What error? Unknown.
&lt;/span&gt;
&lt;span class="c1"&gt;# ✅ Specific handling
&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;db_select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contests&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_info&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: Production incidents hard to debug → increased MTTR&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Empty Library &lt;code&gt;__init__.py&lt;/code&gt; Files
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: llm-router, supabase-async, telegram-agent had empty &lt;code&gt;__init__.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ Before (empty file)
# __init__.py
&lt;/span&gt;
&lt;span class="c1"&gt;# ✅ After
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llm_router&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLMRouter&lt;/span&gt;
&lt;span class="n"&gt;__version__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.1.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;__all__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LLMRouter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: Import failures after pip install&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Indentation Error in Exception Handling
&lt;/h3&gt;

&lt;p&gt;DB operations in ai-insight-curator's processor.py were outside try block → exceptions unhandled&lt;/p&gt;

&lt;h2&gt;
  
  
  Medium Issues - 18
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Dependency Version Mismatches
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Anthropic: 0.25.0 / 0.34.0 → unified to 0.34.0&lt;/li&gt;
&lt;li&gt;Supabase: 2.0.0 / 2.4.0 → unified to 2.4.0&lt;/li&gt;
&lt;li&gt;Python: 3.9 / 3.11 → unified to 3.11 (3.9 EOL: Oct 2025)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Missing Input Validation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/contests?status=invalid&amp;amp;limit=999&lt;/code&gt; accepted without checks&lt;/li&gt;
&lt;li&gt;Fixed: status enum validation, limit range (1-100)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Documentation Drift
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ai-insight-curator README mentioned FastAPI → actually pure Telegram Bot&lt;/li&gt;
&lt;li&gt;Implementation status unclear&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stats
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;New commits&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Files modified&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code deleted&lt;/td&gt;
&lt;td&gt;347 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code added&lt;/td&gt;
&lt;td&gt;200 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tests passed&lt;/td&gt;
&lt;td&gt;91/91 files ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Key Lessons
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Security from day one&lt;/strong&gt;: Add &lt;code&gt;.env&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt; before first commit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit versioning&lt;/strong&gt;: Pin all dependencies (avoid &lt;code&gt;&amp;gt;=&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specific exceptions&lt;/strong&gt;: Use &lt;code&gt;HTTPError&lt;/code&gt;, &lt;code&gt;ValueError&lt;/code&gt; — never bare &lt;code&gt;Exception&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regular audits&lt;/strong&gt;: Schedule security reviews every 3-6 months&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Action Checklist
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Urgent (24 hours)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Rotate API keys (Anthropic/Supabase/Telegram)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;High (1 week)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Verify SSL verification is enabled everywhere&lt;/li&gt;
&lt;li&gt;[ ] Replace broad &lt;code&gt;Exception&lt;/code&gt; catches with specific types&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Medium (2 weeks)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Audit all exception handling&lt;/li&gt;
&lt;li&gt;[ ] Set up quarterly security reviews&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ongoing&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Document lessons learned&lt;/li&gt;
&lt;li&gt;[ ] Apply to next projects&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;In 3 months: 23 issues found and fixed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If we'd done security right from day one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Audit time: 0 hours&lt;/li&gt;
&lt;li&gt;Cost: $0&lt;/li&gt;
&lt;li&gt;Deployment delays: 0 days&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The most important step: Start now.&lt;/strong&gt; Every fix prevents future incidents.&lt;/p&gt;

</description>
      <category>security</category>
      <category>python</category>
      <category>audit</category>
      <category>devops</category>
    </item>
    <item>
      <title>supabase-async: ThreadPoolExecutor에서 httpx AsyncClient로 리팩토링</title>
      <dc:creator>JustJinoIT</dc:creator>
      <pubDate>Sat, 06 Jun 2026 09:35:26 +0000</pubDate>
      <link>https://dev.to/justjinoit/supabase-async-threadpoolexecutoreseo-httpx-asyncclientro-ripaegtoring-2nac</link>
      <guid>https://dev.to/justjinoit/supabase-async-threadpoolexecutoreseo-httpx-asyncclientro-ripaegtoring-2nac</guid>
      <description>&lt;p&gt;&lt;strong&gt;Published on&lt;/strong&gt;: 2026-06-06&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time&lt;/strong&gt;: 6 min&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Tags&lt;/strong&gt;: #python #async #performance #optimization&lt;/p&gt;
&lt;h2&gt;
  
  
  문제: 거짓 비동기
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;supabase-async&lt;/code&gt; 라이브러리는 이름은 async이지만, 실제로는 ThreadPoolExecutor로 동기 호출을 래핑하고 있었습니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ 거짓 비동기 (기존 코드)
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SupabaseAsync&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_event_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_in_executor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_executor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 동기 호출을 async로 포장
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;문제점&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;최대 3개 동시 요청만 가능 (동시성 부족)&lt;/li&gt;
&lt;li&gt;스레드 오버헤드 (각 요청마다 스레드 생성)&lt;/li&gt;
&lt;li&gt;높은 메모리 사용량&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  해결책: httpx AsyncClient
&lt;/h2&gt;

&lt;p&gt;진정한 비동기 HTTP 클라이언트인 httpx를 사용합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ✅ 진정한 비동기 (수정된 코드)
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SupabaseAsync&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;limits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Limits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_connections&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_get_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  성능 개선
&lt;/h2&gt;

&lt;h3&gt;
  
  
  동시성 비교
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;지표&lt;/th&gt;
&lt;th&gt;ThreadPoolExecutor(3)&lt;/th&gt;
&lt;th&gt;httpx(10)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;최대 동시 요청&lt;/td&gt;
&lt;td&gt;3개&lt;/td&gt;
&lt;td&gt;10개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;평균 응답 시간&lt;/td&gt;
&lt;td&gt;450ms&lt;/td&gt;
&lt;td&gt;150ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;메모리 사용량&lt;/td&gt;
&lt;td&gt;250MB&lt;/td&gt;
&lt;td&gt;180MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;초당 처리량&lt;/td&gt;
&lt;td&gt;6.7 req/s&lt;/td&gt;
&lt;td&gt;20 req/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  벤치마크
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 100개 동시 요청 처리 시간
&lt;/span&gt;&lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="n"&gt;초&lt;/span&gt;
&lt;span class="n"&gt;httpx&lt;/span&gt; &lt;span class="n"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;초&lt;/span&gt;
&lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;배&lt;/span&gt; &lt;span class="n"&gt;빠름&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  마이그레이션 단계
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. 클라이언트 초기화
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;limits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Limits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_connections&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_keepalive_connections&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. 요청 메서드
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_get_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Context Manager 지원
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aclose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__aenter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__aexit__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_tb&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# 사용법
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;SupabaseAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contests&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  실제 적용 결과
&lt;/h2&gt;

&lt;p&gt;contest-agent에서 사용:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;요청 성능 개선: 450ms → 150ms
메모리 절감: 250MB → 180MB (-28%)
동시성 증가: 3 → 10 (+233%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  주의사항
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Connection pooling&lt;/strong&gt;: httpx는 자동으로 커넥션 재사용&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exception handling&lt;/strong&gt;: requests.HTTPError → httpx.HTTPError&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeout&lt;/strong&gt;: requests의 timeout 호환 유지&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  결론
&lt;/h2&gt;

&lt;p&gt;ThreadPoolExecutor는 "async 모양"만 냈지만, httpx AsyncClient는 진정한 비동기 I/O를 제공합니다. 동시성이 3배 향상되고 메모리 사용량도 줄어듭니다.&lt;/p&gt;

&lt;p&gt;특히 &lt;strong&gt;높은 동시 요청이 필요한 서비스&lt;/strong&gt;(크롤링, API 게이트웨이)에서 큰 효과를 볼 수 있습니다.&lt;/p&gt;

</description>
      <category>python</category>
      <category>async</category>
      <category>performance</category>
      <category>optimization</category>
    </item>
    <item>
      <title>6개 프로젝트 보안 감사: 25개 이슈 발견 수정 기록</title>
      <dc:creator>JustJinoIT</dc:creator>
      <pubDate>Sat, 06 Jun 2026 09:35:25 +0000</pubDate>
      <link>https://dev.to/justjinoit/6gae-peurojegteu-boan-gamsa-25gae-isyu-balgyeon-sujeong-girog-6e4</link>
      <guid>https://dev.to/justjinoit/6gae-peurojegteu-boan-gamsa-25gae-isyu-balgyeon-sujeong-girog-6e4</guid>
      <description>&lt;p&gt;&lt;strong&gt;Published on&lt;/strong&gt;: 2026-06-06&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time&lt;/strong&gt;: 8 min&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Tags&lt;/strong&gt;: #security #python #audit #devops&lt;/p&gt;
&lt;h2&gt;
  
  
  개요
&lt;/h2&gt;

&lt;p&gt;3개월에 걸쳐 개발한 6개 Python 프로젝트(3개 봇 + 3개 라이브러리)를 종합 감사했습니다. 25개 보안/코드 이슈를 발견했고, 23개를 즉시 수정했습니다. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;감사 대상&lt;/strong&gt;: FastAPI + Telegram Bot + LLM 통합 시스템&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;총 파일&lt;/strong&gt;: 91개 Python 파일&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;발견 이슈&lt;/strong&gt;: 25개 (심각 5개, 중간 18개, 경미 2개)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;수정율&lt;/strong&gt;: 92% (23/25)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  심각도 높음 - 5개 이슈
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. API 키가 Git 히스토리에 노출됨 🔴
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;문제&lt;/strong&gt;: Anthropic, Supabase, Telegram API 키가 &lt;code&gt;.env&lt;/code&gt; 파일로 커밋됨&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ 노출된 상태 (git log에서 확인 가능)
&lt;/span&gt;&lt;span class="n"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ant&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;api03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;xxxxxxxxxx&lt;/span&gt;
&lt;span class="n"&gt;SUPABASE_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sb_publishable_xxxxxxxxxx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;위험도&lt;/strong&gt;: 누구든 이전 커밋으로 API 키 접근 가능 → 리소스 도용, 데이터 침해&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;해결책&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. BFG로 히스토리 정리&lt;/span&gt;
bfg &lt;span class="nt"&gt;--delete-files&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="nt"&gt;--no-blob-protection&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# 2. Git에서 제거&lt;/span&gt;
git &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;--cached&lt;/span&gt; .env
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore

&lt;span class="c"&gt;# 3. API 키 로테이션 (필수)&lt;/span&gt;
&lt;span class="c"&gt;# - Anthropic: console.anthropic.com/account/keys&lt;/span&gt;
&lt;span class="c"&gt;# - Supabase: app.supabase.com → Settings → API&lt;/span&gt;
&lt;span class="c"&gt;# - Telegram: @BotFather → /token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. SSL 검증 비활성화 (MITM 공격 위험) 🔴
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;문제&lt;/strong&gt;: requests 호출에 &lt;code&gt;verify=False&lt;/code&gt; 사용 (10곳)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ 위험한 코드
&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ✅ 안전한 코드
&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 기본값
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;영향&lt;/strong&gt;: HTTPS 중간자 공격(MITM) 가능 → 민감한 데이터 도청&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;수정&lt;/strong&gt;: contest-agent, supabase-async 전체 10곳 제거&lt;/p&gt;

&lt;h3&gt;
  
  
  3. 광범위한 예외 처리 🔴
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;문제&lt;/strong&gt;: &lt;code&gt;except Exception&lt;/code&gt; 으로 모든 오류를 무시 (114곳)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ 버그 추적 불가
&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;db_select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contests&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 어떤 오류인지 알 수 없음
&lt;/span&gt;
&lt;span class="c1"&gt;# ✅ 구체적인 처리
&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;db_select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contests&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_info&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;영향&lt;/strong&gt;: 버그 원인 파악 불가 → 프로덕션 문제 대응 시간 증가&lt;/p&gt;

&lt;h3&gt;
  
  
  4. 라이브러리 &lt;code&gt;__init__.py&lt;/code&gt; 부실
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;문제&lt;/strong&gt;: llm-router, supabase-async, telegram-agent의 &lt;code&gt;__init__.py&lt;/code&gt; 비어있음&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ 기존 (빈 파일)
# __init__.py
# (아무것도 없음)
&lt;/span&gt;
&lt;span class="c1"&gt;# ✅ 수정 후
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llm_router&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLMRouter&lt;/span&gt;
&lt;span class="n"&gt;__version__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.1.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;__all__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LLMRouter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;영향&lt;/strong&gt;: PyPI 설치 후 import 실패&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llm_router&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLMRouter&lt;/span&gt;  &lt;span class="c1"&gt;# ❌ ImportError
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. 문법 오류 (try-except 들여쓰기)
&lt;/h3&gt;

&lt;p&gt;ai-insight-curator의 processor.py에서 DB 작업이 try 블록 밖에 있었음 → 예외 처리 안 됨&lt;/p&gt;

&lt;h2&gt;
  
  
  심각도 중간 - 18개 이슈
&lt;/h2&gt;

&lt;h3&gt;
  
  
  의존성 버전 불일치
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Anthropic: 0.25.0 / 0.34.0 혼재 → 0.34.0으로 통일&lt;/li&gt;
&lt;li&gt;Supabase: 2.0.0 / 2.4.0 혼재 → 2.4.0으로 통일&lt;/li&gt;
&lt;li&gt;Python: 3.9 / 3.11 혼재 → 3.11로 통일 (3.9 EOL: 2025-10)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  입력 검증 부재
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/contests?status=invalid&amp;amp;limit=999&lt;/code&gt; 같은 잘못된 입력 허용&lt;/li&gt;
&lt;li&gt;해결: status enum, limit 1-100 범위 검증 추가&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  README와 코드 불일치
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ai-insight-curator README에 FastAPI 언급 → 실제로는 순수 Telegram Bot&lt;/li&gt;
&lt;li&gt;구현 상태 명시 누락&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  통계
&lt;/h2&gt;

&lt;h3&gt;
  
  
  변경 내역
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;수치&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;신규 커밋&lt;/td&gt;
&lt;td&gt;15개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수정 파일&lt;/td&gt;
&lt;td&gt;22개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;삭제된 코드&lt;/td&gt;
&lt;td&gt;347줄&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;추가된 코드&lt;/td&gt;
&lt;td&gt;200줄&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  테스트 결과
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✅ 91개 파일 Python 문법 검증 통과
✅ 라이브러리 import 테스트 3/3 통과
✅ 프로젝트 구조 검증 3/3 통과
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  배운 점
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;초기부터 보안&lt;/strong&gt;: &lt;code&gt;.env&lt;/code&gt;는 처음부터 &lt;code&gt;.gitignore&lt;/code&gt;에 넣기&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;버전 관리&lt;/strong&gt;: 모든 패키지를 명시적으로 고정 (&amp;gt;=는 피하기)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;구체적인 예외&lt;/strong&gt;: &lt;code&gt;Exception&lt;/code&gt; 대신 &lt;code&gt;HTTPError&lt;/code&gt;, &lt;code&gt;ValueError&lt;/code&gt; 등 구체적으로&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;정기 감사&lt;/strong&gt;: 3-6개월마다 보안 감사 실행&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  다음 단계
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;API 키 로테이션&lt;/strong&gt; (필수)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;모니터링 설정&lt;/strong&gt; (로그 수집, 알림)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;자동 테스트&lt;/strong&gt; (pytest 70% 커버리지)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD 강화&lt;/strong&gt; (GitHub Actions lint + test)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  실행 체크리스트
&lt;/h2&gt;

&lt;p&gt;위험도별 즉시 조치:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;긴급&lt;/strong&gt; (24시간): API 키 로테이션&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;높음&lt;/strong&gt; (1주): SSL 검증 활성화 확인&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;중간&lt;/strong&gt; (2주): 예외처리 감사 (특정 예외로 변경)&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;진행&lt;/strong&gt;: 정기 감사 일정 (분기별)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  결론
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;3개월 개발 = 23개 이슈 발견 + 수정&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;초기부터 보안을 제대로 했다면:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;감사 시간: 0시간&lt;/li&gt;
&lt;li&gt;수정 비용: $0&lt;/li&gt;
&lt;li&gt;배포 지연: 0일&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;지금이라도 시작하면:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;향후 감사는 훨씬 빠름&lt;/li&gt;
&lt;li&gt;버그 감소 → 운영 비용 절감&lt;/li&gt;
&lt;li&gt;채용 시 신뢰도 향상&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;가장 중요한 건 "지금 시작하기"입니다.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>python</category>
      <category>audit</category>
      <category>devops</category>
    </item>
    <item>
      <title>supabase-async: ThreadPoolExecutor에서 httpx AsyncClient로 리팩토링</title>
      <dc:creator>JustJinoIT</dc:creator>
      <pubDate>Sat, 06 Jun 2026 09:26:16 +0000</pubDate>
      <link>https://dev.to/justjinoit/supabase-async-threadpoolexecutoreseo-httpx-asyncclientro-ripaegtoring-4jg9</link>
      <guid>https://dev.to/justjinoit/supabase-async-threadpoolexecutoreseo-httpx-asyncclientro-ripaegtoring-4jg9</guid>
      <description>&lt;p&gt;&lt;strong&gt;Published on&lt;/strong&gt;: 2026-06-06&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time&lt;/strong&gt;: 6 min&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Tags&lt;/strong&gt;: #python #async #performance #optimization&lt;/p&gt;
&lt;h2&gt;
  
  
  문제: 거짓 비동기
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;supabase-async&lt;/code&gt; 라이브러리는 이름은 async이지만, 실제로는 ThreadPoolExecutor로 동기 호출을 래핑하고 있었습니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ 거짓 비동기 (기존 코드)
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SupabaseAsync&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_event_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_in_executor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_executor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 동기 호출을 async로 포장
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;문제점&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;최대 3개 동시 요청만 가능 (동시성 부족)&lt;/li&gt;
&lt;li&gt;스레드 오버헤드 (각 요청마다 스레드 생성)&lt;/li&gt;
&lt;li&gt;높은 메모리 사용량&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  해결책: httpx AsyncClient
&lt;/h2&gt;

&lt;p&gt;진정한 비동기 HTTP 클라이언트인 httpx를 사용합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ✅ 진정한 비동기 (수정된 코드)
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SupabaseAsync&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;limits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Limits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_connections&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_get_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  성능 개선
&lt;/h2&gt;

&lt;h3&gt;
  
  
  동시성 비교
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;지표&lt;/th&gt;
&lt;th&gt;ThreadPoolExecutor(3)&lt;/th&gt;
&lt;th&gt;httpx(10)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;최대 동시 요청&lt;/td&gt;
&lt;td&gt;3개&lt;/td&gt;
&lt;td&gt;10개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;평균 응답 시간&lt;/td&gt;
&lt;td&gt;450ms&lt;/td&gt;
&lt;td&gt;150ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;메모리 사용량&lt;/td&gt;
&lt;td&gt;250MB&lt;/td&gt;
&lt;td&gt;180MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;초당 처리량&lt;/td&gt;
&lt;td&gt;6.7 req/s&lt;/td&gt;
&lt;td&gt;20 req/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  벤치마크
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 100개 동시 요청 처리 시간
&lt;/span&gt;&lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="n"&gt;초&lt;/span&gt;
&lt;span class="n"&gt;httpx&lt;/span&gt; &lt;span class="n"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;초&lt;/span&gt;
&lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;배&lt;/span&gt; &lt;span class="n"&gt;빠름&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  마이그레이션 단계
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. 클라이언트 초기화
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;limits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Limits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_connections&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_keepalive_connections&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. 요청 메서드
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_get_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Context Manager 지원
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aclose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__aenter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__aexit__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_tb&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# 사용법
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;SupabaseAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contests&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  실제 적용 결과
&lt;/h2&gt;

&lt;p&gt;contest-agent에서 사용:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;요청 성능 개선: 450ms → 150ms
메모리 절감: 250MB → 180MB (-28%)
동시성 증가: 3 → 10 (+233%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  주의사항
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Connection pooling&lt;/strong&gt;: httpx는 자동으로 커넥션 재사용&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exception handling&lt;/strong&gt;: requests.HTTPError → httpx.HTTPError&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeout&lt;/strong&gt;: requests의 timeout 호환 유지&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  결론
&lt;/h2&gt;

&lt;p&gt;ThreadPoolExecutor는 "async 모양"만 냈지만, httpx AsyncClient는 진정한 비동기 I/O를 제공합니다. 동시성이 3배 향상되고 메모리 사용량도 줄어듭니다.&lt;/p&gt;

&lt;p&gt;특히 &lt;strong&gt;높은 동시 요청이 필요한 서비스&lt;/strong&gt;(크롤링, API 게이트웨이)에서 큰 효과를 볼 수 있습니다.&lt;/p&gt;

</description>
      <category>python</category>
      <category>async</category>
      <category>performance</category>
      <category>optimization</category>
    </item>
    <item>
      <title>6개 프로젝트 보안 감사: 25개 이슈 발견 수정 기록</title>
      <dc:creator>JustJinoIT</dc:creator>
      <pubDate>Sat, 06 Jun 2026 09:26:14 +0000</pubDate>
      <link>https://dev.to/justjinoit/6gae-peurojegteu-boan-gamsa-25gae-isyu-balgyeon-sujeong-girog-3o6f</link>
      <guid>https://dev.to/justjinoit/6gae-peurojegteu-boan-gamsa-25gae-isyu-balgyeon-sujeong-girog-3o6f</guid>
      <description>&lt;p&gt;&lt;strong&gt;Published on&lt;/strong&gt;: 2026-06-06&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time&lt;/strong&gt;: 8 min&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Tags&lt;/strong&gt;: #security #python #audit #devops&lt;/p&gt;
&lt;h2&gt;
  
  
  개요
&lt;/h2&gt;

&lt;p&gt;3개월에 걸쳐 개발한 6개 Python 프로젝트(3개 봇 + 3개 라이브러리)를 종합 감사했습니다. 25개 보안/코드 이슈를 발견했고, 23개를 즉시 수정했습니다. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;감사 대상&lt;/strong&gt;: FastAPI + Telegram Bot + LLM 통합 시스템&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;총 파일&lt;/strong&gt;: 91개 Python 파일&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;발견 이슈&lt;/strong&gt;: 25개 (심각 5개, 중간 18개, 경미 2개)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;수정율&lt;/strong&gt;: 92% (23/25)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  심각도 높음 - 5개 이슈
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. API 키가 Git 히스토리에 노출됨 🔴
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;문제&lt;/strong&gt;: Anthropic, Supabase, Telegram API 키가 &lt;code&gt;.env&lt;/code&gt; 파일로 커밋됨&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ 노출된 상태 (git log에서 확인 가능)
&lt;/span&gt;&lt;span class="n"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ant&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;api03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;xxxxxxxxxx&lt;/span&gt;
&lt;span class="n"&gt;SUPABASE_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sb_publishable_xxxxxxxxxx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;위험도&lt;/strong&gt;: 누구든 이전 커밋으로 API 키 접근 가능 → 리소스 도용, 데이터 침해&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;해결책&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. BFG로 히스토리 정리&lt;/span&gt;
bfg &lt;span class="nt"&gt;--delete-files&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="nt"&gt;--no-blob-protection&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# 2. Git에서 제거&lt;/span&gt;
git &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;--cached&lt;/span&gt; .env
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore

&lt;span class="c"&gt;# 3. API 키 로테이션 (필수)&lt;/span&gt;
&lt;span class="c"&gt;# - Anthropic: console.anthropic.com/account/keys&lt;/span&gt;
&lt;span class="c"&gt;# - Supabase: app.supabase.com → Settings → API&lt;/span&gt;
&lt;span class="c"&gt;# - Telegram: @BotFather → /token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. SSL 검증 비활성화 (MITM 공격 위험) 🔴
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;문제&lt;/strong&gt;: requests 호출에 &lt;code&gt;verify=False&lt;/code&gt; 사용 (10곳)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ 위험한 코드
&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ✅ 안전한 코드
&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 기본값
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;영향&lt;/strong&gt;: HTTPS 중간자 공격(MITM) 가능 → 민감한 데이터 도청&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;수정&lt;/strong&gt;: contest-agent, supabase-async 전체 10곳 제거&lt;/p&gt;

&lt;h3&gt;
  
  
  3. 광범위한 예외 처리 🔴
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;문제&lt;/strong&gt;: &lt;code&gt;except Exception&lt;/code&gt; 으로 모든 오류를 무시 (114곳)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ 버그 추적 불가
&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;db_select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contests&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 어떤 오류인지 알 수 없음
&lt;/span&gt;
&lt;span class="c1"&gt;# ✅ 구체적인 처리
&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;db_select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contests&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_info&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;영향&lt;/strong&gt;: 버그 원인 파악 불가 → 프로덕션 문제 대응 시간 증가&lt;/p&gt;

&lt;h3&gt;
  
  
  4. 라이브러리 &lt;code&gt;__init__.py&lt;/code&gt; 부실
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;문제&lt;/strong&gt;: llm-router, supabase-async, telegram-agent의 &lt;code&gt;__init__.py&lt;/code&gt; 비어있음&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ 기존 (빈 파일)
# __init__.py
# (아무것도 없음)
&lt;/span&gt;
&lt;span class="c1"&gt;# ✅ 수정 후
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llm_router&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLMRouter&lt;/span&gt;
&lt;span class="n"&gt;__version__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.1.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;__all__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LLMRouter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;영향&lt;/strong&gt;: PyPI 설치 후 import 실패&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llm_router&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLMRouter&lt;/span&gt;  &lt;span class="c1"&gt;# ❌ ImportError
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. 문법 오류 (try-except 들여쓰기)
&lt;/h3&gt;

&lt;p&gt;ai-insight-curator의 processor.py에서 DB 작업이 try 블록 밖에 있었음 → 예외 처리 안 됨&lt;/p&gt;

&lt;h2&gt;
  
  
  심각도 중간 - 18개 이슈
&lt;/h2&gt;

&lt;h3&gt;
  
  
  의존성 버전 불일치
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Anthropic: 0.25.0 / 0.34.0 혼재 → 0.34.0으로 통일&lt;/li&gt;
&lt;li&gt;Supabase: 2.0.0 / 2.4.0 혼재 → 2.4.0으로 통일&lt;/li&gt;
&lt;li&gt;Python: 3.9 / 3.11 혼재 → 3.11로 통일 (3.9 EOL: 2025-10)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  입력 검증 부재
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/contests?status=invalid&amp;amp;limit=999&lt;/code&gt; 같은 잘못된 입력 허용&lt;/li&gt;
&lt;li&gt;해결: status enum, limit 1-100 범위 검증 추가&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  README와 코드 불일치
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ai-insight-curator README에 FastAPI 언급 → 실제로는 순수 Telegram Bot&lt;/li&gt;
&lt;li&gt;구현 상태 명시 누락&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  통계
&lt;/h2&gt;

&lt;h3&gt;
  
  
  변경 내역
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;수치&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;신규 커밋&lt;/td&gt;
&lt;td&gt;15개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수정 파일&lt;/td&gt;
&lt;td&gt;22개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;삭제된 코드&lt;/td&gt;
&lt;td&gt;347줄&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;추가된 코드&lt;/td&gt;
&lt;td&gt;200줄&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  테스트 결과
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✅ 91개 파일 Python 문법 검증 통과
✅ 라이브러리 import 테스트 3/3 통과
✅ 프로젝트 구조 검증 3/3 통과
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  배운 점
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;초기부터 보안&lt;/strong&gt;: &lt;code&gt;.env&lt;/code&gt;는 처음부터 &lt;code&gt;.gitignore&lt;/code&gt;에 넣기&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;버전 관리&lt;/strong&gt;: 모든 패키지를 명시적으로 고정 (&amp;gt;=는 피하기)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;구체적인 예외&lt;/strong&gt;: &lt;code&gt;Exception&lt;/code&gt; 대신 &lt;code&gt;HTTPError&lt;/code&gt;, &lt;code&gt;ValueError&lt;/code&gt; 등 구체적으로&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;정기 감사&lt;/strong&gt;: 3-6개월마다 보안 감사 실행&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  다음 단계
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;API 키 로테이션&lt;/strong&gt; (필수)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;모니터링 설정&lt;/strong&gt; (로그 수집, 알림)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;자동 테스트&lt;/strong&gt; (pytest 70% 커버리지)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD 강화&lt;/strong&gt; (GitHub Actions lint + test)&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;결론&lt;/strong&gt;: 3개월 개발에서 23개 이슈를 발견하고 수정했습니다. 특히 보안(API 키, SSL)은 즉시 로테이션이 필요합니다. 정기적인 감사로 프로덕션 안정성을 지속적으로 개선할 수 있습니다.&lt;/p&gt;

</description>
      <category>security</category>
      <category>python</category>
      <category>audit</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
