Jekyll에 Giscus로 comment 기능 추가
Jekyll에 Giscus를 추가하는 방법은 간단하며, 아래 단계를 따르면 됩니다.
1. Giscus 설정하기
Giscus는 GitHub Discussions를 이용한 댓글 시스템입니다. 먼저 Giscus를 설정해야 합니다.
- Giscus 공식 웹사이트에 접속합니다.
- 아래 정보를 입력합니다: • Repository: 댓글을 저장할 GitHub 저장소 선택 (예: your-username/your-repo) • Discussion Category: Giscus에서 사용할 Discussion 카테고리 선택 • Mapping: 페이지와 댓글을 연결하는 방식 선택 (예: pathname) • Reaction`: 사용자가 댓글에 반응할 수 있도록 활성화할지 여부 설정 • Theme: Giscus 위젯의 테마 설정 (예: light, dark) • 기타 필요 옵션 설정 후 Code Snippet 복사.
2. Jekyll 테마 수정
Giscus 스크립트를 Jekyll 블로그의 적절한 위치에 추가합니다.
(1) _layouts 파일 수정
Giscus를 추가할 위치에 따라 주로 post.html 또는 default.html 파일을 수정합니다.
예: _layouts/post.html
<article>
<article class="post h-entry" itemscope itemtype="http://schema.org/BlogPosting">
<header class="post-header">
<h1 class="post-title p-name" itemprop="name headline">Tor로 각기 다른 proxy 서버에서 api 호출하기</h1>
<p class="post-meta">
<time class="dt-published" datetime="2025-01-16T16:57:37+00:00" itemprop="datePublished">Jan 16, 2025
</time></p>
</header><img src="/assets/images/posts/thumbnails/2025-01-17-tor.png" alt="Tor로 각기 다른 proxy 서버에서 api 호출하기" class="post-thumbnail" itemprop="image"><div class="post-content e-content" itemprop="articleBody">
<h1 id="tor로-각기-다른-proxy-서버에서-api-호출하기">Tor로 각기 다른 proxy 서버에서 api 호출하기</h1>
<p>특정 웹사이트를 스크랩핑할 때, 너무 많은 호출을 하면, block하는 경우가 있다.
이걸 우회하기 위해, 각기 다른 proxy 서버에서 api를 호출하는 방법을 설명.</p>
<h2 id="대규모-api-호출-목적으로-tor-proxy-장단점">대규모 api 호출 목적으로 tor proxy 장단점</h2>
<h3 id="tor를-사용하는-이유">Tor를 사용하는 이유</h3>
<ul>
<li>무료</li>
<li>작동하는 proxy 서버가 일반 무료 proxy서버보다 상당히 많음</li>
<li>무료 Proxy 서버를 여기저기 찾을 필요 없어서, 초기 가벼운 테스트에 용이</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>익명성이 주요 목적이라, 여러 노드를 거치면서, 속도가 느림</li>
<li>하나의 tor 서비스 실행 당, 하나의 proxy 서버 밖에 사용 못해서, api를 병렬로 호출하려면, 여러 tor를 돌려야 해서, 대규모 호출에 적절하지 않음</li>
</ul>
<h3 id="일반-proxy-서버의-특징">일반 proxy 서버의 특징</h3>
<ul>
<li>공개된 무료 서버는 호출하려는 api서버를 이미 다른 사용자들이 많이 호출해서, block 되거나, 서버 자체가 작동 안하는 경우가 많음.</li>
<li>실제 https://free-proxy-list.net/ 에서 300개 무료 서버를 테스트해본 결과, 8개 서버가 작동 했지만, 한번 호출 후, block되서, 4번의 유효 api호출만 가능했음</li>
<li>하지만, 좋은 proxy 서버를 찾을 수 있다면, tor처럼, 병렬 호출을 위해 여러 tor 서비스를 가동할 필요 없이, 간단한 api 호출만으로 병렬 호출이 가능함</li>
</ul>
<h2 id="mac에서-사용">Mac에서 사용</h2>
<p>설치</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew install tor
</code></pre></div></div>
<p>실행</p>
<ul>
<li>torrc configuration 파일을 통해 설정 가능
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tor -f <torrc-file-path>
</code></pre></div> </div>
</li>
</ul>
<h2 id="python에서-tor-경유-api-호출-방법">python에서 tor 경유 API 호출 방법</h2>
<p>tor가 로컬에 돌아가기 때문에, 127.0.0.1로 설정하고,
tor 기본 포트인 9050로 설정한다</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="k">def</span> <span class="nf">api_call</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">proxy</span><span class="p">):</span>
<span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">proxies</span><span class="o">=</span><span class="p">{</span>
<span class="s">"https"</span><span class="p">:</span> <span class="sa">f</span><span class="s">"socks5://</span><span class="si">{</span><span class="n">proxy</span><span class="si">}</span><span class="s">"</span><span class="p">,</span>
<span class="s">"http"</span><span class="p">:</span> <span class="sa">f</span><span class="s">"socks5://</span><span class="si">{</span><span class="n">proxy</span><span class="si">}</span><span class="s">"</span><span class="p">,</span>
<span class="p">})</span>
<span class="n">api_call</span><span class="p">(</span><span class="s">"api-to-call"</span><span class="p">,</span> <span class="s">"127.0.0.1:9050"</span><span class="p">)</span>
</code></pre></div></div>
<p>socks5 protoccol 사용을 위해, socks 설치 필요.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip install requests[socks]
</code></pre></div></div>
<h2 id="exit-node-제어-방법">exit node 제어 방법</h2>
<p>tor는 익명성을 위해 여러 노드를 거쳐서 api를 호출하는데,
api호출시 블럭 여부는 api 서버에서 ip를 알 수 있는 exit node의 제어가 중요하므로, exit 노드를 제어하는 방법을 설명</p>
<h3 id="torrc-에-제어를-위한-port-추가">torrc 에 제어를 위한 port 추가</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ControlPort 9051
HashedControlPassword <password-hash>
CookieAuthentication 0
</code></pre></div></div>
<h3 id="비밀번호-해시-생성-방법">비밀번호 해시 생성 방법</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tor --hash-password "your_password"
</code></pre></div></div>
<h3 id="python에서-tor-exit-node-제어하기">python에서 tor exit node 제어하기</h3>
<h4 id="exit-node-설정하기">exit node 설정하기</h4>
<ul>
<li>exit node의 fingerprint로 설정
```python
from stem.control import Controller</li>
</ul>
<p>TOR_CONTROL_PORT = 9051
TOR_PASSWORD = ‘your_password’
def set_exit_node(fingerprint):
“””
특정 exit 노드를 Tor 네트워크에 설정.
“””
with Controller.from_port(port=TOR_CONTROL_PORT) as controller:
controller.authenticate(password=TOR_PASSWORD)
controller.set_options({
‘ExitNodes’: fingerprint,
‘StrictNodes’: ‘1’
})
print(f”Exit node set to: {fingerprint}”)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
#### 유효한 exit node 가져오기
```python
import requests
def fetch_exit_node_fingerprints():
url = "https://onionoo.torproject.org/details?flag=Exit"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
exit_fingerprints = [relay['fingerprint'] for relay in data['relays']]
return exit_fingerprints
except requests.RequestException as e:
print(f"Error fetching data: {e}")
return []
</code></pre></div></div>
<h4 id="api-호출이-block됐을-때-exit-node-교체하기">api 호출이 block됐을 때, exit node 교체하기</h4>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="n">fingerprints</span> <span class="o">=</span> <span class="n">fetch_exit_node_fingerprints</span><span class="p">()</span>
<span class="k">for</span> <span class="n">fingerprint</span> <span class="ow">in</span> <span class="n">fingerprints</span><span class="p">:</span>
<span class="n">set_exit_node</span><span class="p">(</span><span class="n">fingerprint</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1000</span><span class="p">):</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">api_call</span><span class="p">(</span><span class="s">'api-to-call'</span><span class="p">,</span> <span class="s">"127.0.0.1:9050"</span><span class="p">)</span>
<span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span> <span class="c1"># check block
</span> <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"move to next by error </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>
</div>
<div class="related-posts">
<h2>Related Posts</h2>
<ul class="related-posts-list">
<li class="related-post-item">
<a href="/infra/security/2026/01/25/linux-security-model.html">
<span class="related-post-title">Linux 서버 보안 모델 완벽 가이드 - 시크릿 관리와 프로세스 격리</span>
<span class="related-post-meta">
<span class="related-post-category">infra</span>
<span class="related-post-date">2026-01-25</span>
</span>
</a>
</li>
<li class="related-post-item">
<a href="/infra/security/2026/01/24/macos-security-model.html">
<span class="related-post-title">macOS 보안 모델 완벽 가이드 - SSH, Keychain, TCC 중심</span>
<span class="related-post-meta">
<span class="related-post-category">infra</span>
<span class="related-post-date">2026-01-24</span>
</span>
</a>
</li>
<li class="related-post-item">
<a href="/infra/devops/2026/01/16/android-hotspot-tethering-speed-fix.html">
<span class="related-post-title">안드로이드 핫스팟 연결 시 맥북 인터넷 먹통/느림 현상 완벽 해결법 (통신사 속도제한 우회: TTL, dun 설정)</span>
<span class="related-post-meta">
<span class="related-post-category">infra</span>
<span class="related-post-date">2026-01-16</span>
</span>
</a>
</li>
<li class="related-post-item">
<a href="/infra/security/2026/01/11/ssl-tls-advanced.html">
<span class="related-post-title">SSL/TLS 심화 - 자체 인증서 발급과 MITM 공격 원리</span>
<span class="related-post-meta">
<span class="related-post-category">infra</span>
<span class="related-post-date">2026-01-11</span>
</span>
</a>
</li>
</ul>
</div>
<style>
.related-posts {
margin-top: 50px;
padding-top: 30px;
border-top: 1px solid #e0e0e0;
}
.related-posts h2 {
margin-bottom: 20px;
font-size: 1.4em;
color: #333;
}
.related-posts-list {
list-style: none;
padding: 0;
margin: 0;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 15px;
}
.related-post-item {
background: #f8f9fa;
border-radius: 8px;
transition: transform 0.2s, box-shadow 0.2s;
}
.related-post-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.related-post-item a {
display: block;
padding: 15px;
text-decoration: none;
color: inherit;
}
.related-post-title {
display: block;
font-weight: 600;
color: #0366d6;
margin-bottom: 8px;
font-size: 0.95em;
line-height: 1.4;
}
.related-post-meta {
display: flex;
justify-content: space-between;
font-size: 0.8em;
color: #666;
}
.related-post-category {
background: #e0e0e0;
padding: 2px 8px;
border-radius: 4px;
text-transform: capitalize;
}
@media (max-width: 600px) {
.related-posts-list {
grid-template-columns: 1fr;
}
}
</style>
<section class="post-comments">
<h2>Comments</h2>
<script src="https://giscus.app/client.js"
data-repo="dss99911/dss99911.github.io"
data-repo-id="R_kgDOK1J8gQ"
data-category="Announcements"
data-category-id="DIC_kwDOK1J8gc4C0Sct"
data-mapping="pathname"
data-strict="0"
data-reactions-enabled="1"
data-emit-metadata="0"
data-input-position="top"
data-theme="preferred_color_scheme"
data-lang="ko"
data-loading="lazy"
crossorigin="anonymous"
async>
</script>
</section>
<style>
.post-comments {
margin-top: 60px;
padding-top: 30px;
border-top: 1px solid #e0e0e0;
}
.post-comments h2 {
margin-bottom: 20px;
font-size: 1.5em;
}
</style>
<a class="u-url" href="/infra/devops/2025/01/16/tor.html" hidden></a>
</article>
</article>
<div id="giscus-comments"></div>
<script src="https://giscus.app/client.js"
data-repo="your-username/your-repo"
data-repo-id="your-repo-id"
data-category="General"
data-category-id="category-id"
data-mapping="pathname"
data-reactions-enabled="1"
data-theme="light"
data-lang="en"
crossorigin="anonymous"
async>
</script>
(2) CSS 수정 (선택 사항)
Giscus 위젯이 블로그의 디자인과 잘 어울리도록 필요 시 추가 CSS를 작성합니다.
3. Jekyll 블로그 빌드 및 확인
- 변경 사항을 저장한 후 Jekyll 블로그를 빌드합니다.
bundle exec jekyll serve
- 로컬 서버를 열어 페이지가 제대로 렌더링되었는지 확인합니다.
- giscus가 작동하지 않는다면 GitHub Discussions 설정 또는 repo, category 등의 매개변수를 다시 확인하세요.
4. 배포
변경 사항을 저장소에 푸시하고 배포 사이트에서도 Giscus가 잘 동작하는지 확인합니다.
참고: Giscus는 GitHub Discussions를 기반으로 하기 때문에 저장소에서 Discussions가 활성화되어 있어야 합니다.
필요한 추가 사항이 있으면 말씀해주세요!
Comments