<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dev-bono.log</title>
        <link>https://velog.io/</link>
        <description>Let`s go!</description>
        <lastBuildDate>Mon, 12 Jan 2026 02:46:15 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dev-bono.log</title>
            <url>https://velog.velcdn.com/images/dev-bono/profile/05b7778e-fc1b-4f08-b361-49bef4727106/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dev-bono.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dev-bono" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Claude Code Hook Message Settings]]></title>
            <link>https://velog.io/@dev-bono/Claude-Code-Hook-Message-Settings</link>
            <guid>https://velog.io/@dev-bono/Claude-Code-Hook-Message-Settings</guid>
            <pubDate>Mon, 12 Jan 2026 02:46:15 GMT</pubDate>
            <description><![CDATA[<h2 id="클로드코드의-특정이벤트를-이용해서-메시지-보내기">클로드코드의 특정이벤트를 이용해서 메시지 보내기</h2>
<h3 id="step-1">Step 1</h3>
<ul>
<li>클로드코드 설정에 훅설정을 추가해준다.</li>
<li>훅의 종류에 따라 원하는 이벤트(command)를 통해 하고자하는 작업을 설정한다.</li>
</ul>
<blockquote>
<p><strong>사용하는 이벤트 목록</strong></p>
</blockquote>
<ul>
<li>Stop (작업완료)</li>
<li>PreToolUse (도구호출 직전)</li>
<li>PostToolUse (도구호출 완료)</li>
</ul>
<h3 id="step-2">Step 2</h3>
<ul>
<li><p>아래 예시는 완료필터(Stop), 작업도구 필터(PreToolUse,PostToolUse) 총 2가지 부류로 필터해 이벤트를 수행하게 해 두었다.</p>
</li>
<li><p><del>PreToolUse에서 자동수행으로 설정하게 되면 도구를 묻지않는 상태도 발생하기 때문에 PreToolUse 이벤트에서 10초의 딜레이를 발생하였다.</del> </p>
</li>
<li><p><del>10초내에 PostToolUse에서 완료처리가 되었다면 이벤트 메시지 발송을 취소하도록 처리하여 불필요한 메시지 전송을 방지하였다.</del></p>
</li>
<li><p>딜레이를 발생시켜도 큐에 담지 않는 이상은 정상적인 처리가 불가하다고 판단하여. 위 기능은 폐기하도록 함.</p>
</li>
<li><p>Bash|Write|Edit는 메시지를 많이 발생시키지만 종종 이 도구들이 확인을 묻는 경우가 발생함. 훅이 확인해야하는지 안해야하는지 까지 판별하지 못하기 때문에 거슬리면 삭제하여 한번씩 확인하던지, 예정/완료 메시지를 교차로 확인해 완료가 안되고 있으면 확인해보던지 둘중 하나의 방법으로 임시 처리함.(나중에 시간되면 더 좋은 방식으로 처리할 예정)</p>
</li>
</ul>
<pre><code>path .claud/settings.json</code></pre><pre><code class="language-json">{
  &quot;alwaysThinkingEnabled&quot;: true,
  &quot;hooks&quot;: {
    &quot;Stop&quot;: [
      {
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;/home/persol/.claude/hooks/telegram-notify.sh&quot;,
            &quot;timeout&quot;: 10000
          }
        ]
      }
    ],
    &quot;PreToolUse&quot;: [
      {
        &quot;matcher&quot;: &quot;Bash|Write|Edit|AskUserQuestion|ExitPlanMode&quot;,
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;/home/persol/.claude/hooks/telegram-notify.sh&quot;,
            &quot;timeout&quot;: 10000
          }
        ]
      }
    ],
    &quot;PostToolUse&quot;: [
      {
        &quot;matcher&quot;: &quot;Bash|Write|Edit|AskUserQuestion|ExitPlanMode&quot;,
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;/home/persol/.claude/hooks/telegram-notify.sh&quot;,
            &quot;timeout&quot;: 10000
          }
        ]
      }
    ]
  }
}</code></pre>
<h3 id="step-3">Step 3</h3>
<p>이후 쉘스크립트로 취향에 맞게 텔레그램,슬랙 등 메시지를 받을 주체를 선택해서 스크립트를 작성하면 된다.</p>
<ul>
<li>실제 운영해보니 Bash|Write|Edit 도구까지 추가하면 너무 많은 메시지가 발송된다.
특별한일 없으면  AskUserQuestion|ExitPlanMode 만 넣어줘도 될 듯 함.</li>
</ul>
<h3 id="샘플-쉘-스크립트텔레그램">샘플 쉘 스크립트(텔레그램)</h3>
<blockquote>
<p>telegram-notify.sh</p>
</blockquote>
<pre><code class="language-bash">#!/bin/bash

# 텔레그램 설정 - 환경변수 또는 직접 설정
TELEGRAM_BOT_TOKEN=&quot;${TELEGRAM_BOT_TOKEN:-8487791962:AAGSMwdIDZDhPX6JYP_MvWoDRvXwee2ikhY}&quot;
TELEGRAM_CHAT_ID=&quot;${TELEGRAM_CHAT_ID:-290471427}&quot;

# stdin에서 JSON 데이터 읽기
INPUT=$(cat)

# 디버깅용 로그 (나중에 삭제)
echo &quot;$INPUT&quot; &gt;&gt; /tmp/claude-hook-debug.log

# grep과 sed를 사용하여 JSON 파싱 (jq 대체)
# Perl 정규식(-P)으로 더 정확한 매칭
get_json_value() {
  echo &quot;$INPUT&quot; | grep -oP &quot;\&quot;$1\&quot;\s*:\s*\&quot;\K[^\&quot;]*&quot; 2&gt;/dev/null | head -1
}

# hook 이벤트 타입 추출
EVENT=$(get_json_value &quot;hook_event_name&quot;)
NOTIFICATION_TYPE=$(get_json_value &quot;type&quot;)
SESSION_ID=$(get_json_value &quot;session_id&quot;)
PERMISSION_MODE=$(get_json_value &quot;permission_mode&quot;)
TOOL_NAME=$(get_json_value &quot;tool_name&quot;)

# cwd에서 프로젝트명 추출
CWD=$(get_json_value &quot;cwd&quot;)
PROJECT_NAME=$(basename &quot;$CWD&quot;)
PROJECT_PREFIX=&quot;&quot;
if [ -n &quot;$PROJECT_NAME&quot; ]; then
  PROJECT_PREFIX=&quot;📁 [$PROJECT_NAME]
&quot;
fi

# 이벤트별 메시지 생성
case &quot;$EVENT&quot; in
  &quot;PreToolUse&quot;)
    MESSAGE=&quot;${PROJECT_PREFIX}[PreToolUse] 🔨 $TOOL_NAME 도구 실행 예정&quot;
    ;;
  &quot;PostToolUse&quot;)
    MESSAGE=&quot;${PROJECT_PREFIX}[PostToolUse] ✅ $TOOL_NAME 도구 완료&quot;
    ;;
  &quot;PermissionRequest&quot;)
    case &quot;$PERMISSION_MODE&quot; in
      &quot;plan&quot;)
        # 플랜 모드에서의 권한 요청은 무시 (Stop에서 처리)
        exit 0
        ;;
      &quot;acceptEdits&quot;)
        # 편집 모드에서의 권한 요청은 무시 (Stop에서 처리)
        exit 0
        ;;
      *)
        # 실제 권한 요청만 알림
        MESSAGE=&quot;${PROJECT_PREFIX}[PermissionRequest] 🔐 $TOOL_NAME 사용 권한이 필요합니다.&quot;
        ;;
    esac
    ;;
  &quot;Notification&quot;)
    TITLE=$(get_json_value &quot;title&quot;)
    BODY=$(get_json_value &quot;body&quot;)

    # 알림 타입별 메시지 생성
    case &quot;$NOTIFICATION_TYPE&quot; in
      &quot;permission_prompt&quot;)
        MESSAGE=&quot;${PROJECT_PREFIX}[Notification] 🔐 권한 승인 필요
확인해주세요.&quot;
        ;;
      &quot;idle_prompt&quot;)
        MESSAGE=&quot;${PROJECT_PREFIX}[Notification] ⏳ 입력 대기 중
입력을 기다리고 있습니다.&quot;
        ;;
      &quot;elicitation_dialog&quot;)
        MESSAGE=&quot;${PROJECT_PREFIX}[Notification] ❓ 선택 필요
$TITLE&quot;
        ;;
      *)
        MESSAGE=&quot;${PROJECT_PREFIX}[Notification] 🔔 $TITLE&quot;
        ;;
    esac
    ;;
  &quot;UserPromptSubmit&quot;)
    PROMPT=$(get_json_value &quot;prompt&quot;)
    # 프롬프트가 길면 앞부분만 표시
    PROMPT_SHORT=$(echo &quot;$PROMPT&quot; | head -c 50)
    if [ ${#PROMPT} -gt 50 ]; then
      PROMPT_SHORT=&quot;${PROMPT_SHORT}...&quot;
    fi
    MESSAGE=&quot;${PROJECT_PREFIX}[UserPromptSubmit] 📝 사용자 입력: $PROMPT_SHORT&quot;
    ;;
  &quot;Stop&quot;)
    # Stop 이벤트는 이벤트명 없이 기존 형식 유지
    STOP_REASON=$(get_json_value &quot;stop_hook_reason&quot;)

    case &quot;$STOP_REASON&quot; in
      &quot;end_turn&quot;)
        MESSAGE=&quot;${PROJECT_PREFIX}✅ 작업 완료
결과를 확인해주세요.&quot;
        ;;
      &quot;user_stop&quot;)
        MESSAGE=&quot;${PROJECT_PREFIX}⏹️ 작업 중단
사용자에 의해 중단되었습니다.&quot;
        ;;
      *)
        # 빈 stop_reason이거나 알 수 없는 경우 = 확인 필요
        MESSAGE=&quot;${PROJECT_PREFIX}🔔 확인 필요
Claude가 대기 중입니다.&quot;
        ;;
    esac
    ;;
  &quot;SubagentStop&quot;)
    MESSAGE=&quot;${PROJECT_PREFIX}[SubagentStop] ⏹️ 서브에이전트 작업 완료&quot;
    ;;
  &quot;SessionStart&quot;)
    SOURCE=$(get_json_value &quot;source&quot;)
    MESSAGE=&quot;${PROJECT_PREFIX}[SessionStart] 🚀 세션 시작 (Source: $SOURCE)&quot;
    ;;
  &quot;SessionEnd&quot;)
    REASON=$(get_json_value &quot;reason&quot;)
    MESSAGE=&quot;${PROJECT_PREFIX}[SessionEnd] 🛑 세션 종료 (Reason: $REASON)&quot;
    ;;
  &quot;PreCompact&quot;)
    TRIGGER=$(get_json_value &quot;trigger&quot;)
    MESSAGE=&quot;${PROJECT_PREFIX}[PreCompact] 📦 컨텍스트 압축 예정 (Trigger: $TRIGGER)&quot;
    ;;
  *)
    # 알 수 없는 이벤트도 일단 알림
    MESSAGE=&quot;${PROJECT_PREFIX}[Unknown] ❓ 알 수 없는 이벤트: $EVENT&quot;
    ;;
esac

# 텔레그램 API로 메시지 전송
if [ -n &quot;$TELEGRAM_BOT_TOKEN&quot; ] &amp;&amp; [ -n &quot;$TELEGRAM_CHAT_ID&quot; ]; then
  echo &quot;[$(date &#39;+%Y-%m-%d %H:%M:%S&#39;)] Sending message: $MESSAGE&quot; &gt;&gt; /tmp/claude-hook-curl.log
  curl -s -X POST &quot;https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage&quot; \
    -d chat_id=&quot;$TELEGRAM_CHAT_ID&quot; \
    -d text=&quot;$MESSAGE&quot; \
    -d parse_mode=&quot;HTML&quot; &gt;&gt; /tmp/claude-hook-curl.log 2&gt;&amp;1
  echo &quot;&quot; &gt;&gt; /tmp/claude-hook-curl.log
fi

exit 0</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Gitlab CI/CD 기초]]></title>
            <link>https://velog.io/@dev-bono/Gitlab-CICD-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@dev-bono/Gitlab-CICD-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Fri, 07 Nov 2025 07:57:05 GMT</pubDate>
            <description><![CDATA[<h3 id="1-배포대상dev-서버-key-생성">1. 배포대상(dev) 서버 key 생성.</h3>
<pre><code class="language-bash"># ex)
ssh-keygen -t ed25519 -C &quot;gitlab-ci&quot;</code></pre>
<h3 id="2-생성된-public-key를-authorized_keys에-추가">2. 생성된 public key를 authorized_keys에 추가.</h3>
<pre><code class="language-bash">cd ~/.ssh
cat id_ed25519.pub
# cat으로 나온 값을 아래 파일에 추가해준다.
vi authorized_keys</code></pre>
<h3 id="3-gitlab-web에-private-key-등록">3. Gitlab Web에 Private Key 등록</h3>
<pre><code class="language-bash"># 1번에서 만들어진 키중 .pub가 없는 private key를 
# [GitlabWeb] 프로젝트 &gt; Settings &gt; CI/CD &gt; Variables 에 추가.
# SSH_PRIVATE_KEY라는 이름으로 private key 내용 저장</code></pre>
<h3 id="4-프로젝트-root에-gitlab-ciyml-파일작성">4. 프로젝트 root에 .gitlab-ci.yml 파일작성</h3>
<pre><code class="language-yaml">stages:
  - deploy

deploy_dev:
  stage: deploy
  only:
    - main        # main 브랜치에만 동작
  variables:
    GIT_STRATEGY: none  # GitLab Runner에서 코드를 clone하지 않음( 이부분을 추가하지 않으면 권한문제가 발생함 이 이후에 다루도록 함 )
  script:
    # 1. SSH 세팅
    - &#39;which ssh-agent || ( apt-get update -y &amp;&amp; apt-get install openssh-client -y )&#39;
    - eval $(ssh-agent -s)
    - echo &quot;$SSH_PRIVATE_KEY&quot; | tr -d &#39;\r&#39; | ssh-add -
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    # 포트가 다를경우 ssh-keyscan 뒤에 -p [포트번호] 추가!
    - ssh-keyscan -H YOUR_SERVER_IP &gt;&gt; ~/.ssh/known_hosts

    # 2. 배포 서버에 접속해 pull/build/restart
    # 포트가 다를경우 ssh 뒤에 -p [포트번호] 추가!
    - ssh USER@YOUR_SERVER_IP &quot;cd /srv/your-app &amp;&amp; git pull &amp;&amp; npm install &amp;&amp; npm run build &amp;&amp; pm2 restart all&quot;</code></pre>
<h3 id="5-gitlab-runner-설치">5. Gitlab Runner 설치</h3>
<p>1) Ubuntu/Debian 기준</p>
<pre><code class="language-bash"># 패키지 설치와 등록
sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
sudo chmod +x /usr/local/bin/gitlab-runner
# 서비스 등록
sudo useradd --comment &#39;GitLab Runner&#39; --create-home gitlab-runner --shell /bin/bash
sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
sudo gitlab-runner start
</code></pre>
<h3 id="6-runner-등록">6. Runner 등록</h3>
<pre><code class="language-bash"># 1. GitlabWeb 접속
# 2. 프로젝트 &gt; Settings &gt; CI/CD &gt; Runners &gt; “Set up a specific runner manually”에서 Registration Token을 복사
# 3. 배포 서버에서 등록 명령어 실행:
sudo gitlab-runner register</code></pre>
<p>질문마다 아래처럼 입력합니다:</p>
<pre><code class="language-bash"># GitLab instance URL( for example,https://gitlab.com/):
- 자신이 쓰는 GitLab 주소. 회사/자체 서버면 해당 주소
# Enter the registration token:
- 복사한 토큰 붙여넣기
# Enter a description for the runner:
- 자유롭게(예: dev-server)
# Enter tags for the runner(comma-separated):
- 필요할 경우만 입력(예: node, prod 등)
# Enter optional maintenance note for the runner:
- (예:개발서버 배포용)
# Enter an executor: docker+machine, kubernetes, instance, ssh, parallels, virtualbox, docker, docker-windows, docker-autoscaler, custom, shell:
- 보통은 shell 선택, (도커 쓰면 docker)</code></pre>
<h3 id="7-설치확인">7. 설치확인</h3>
<pre><code class="language-bash">sudo gitlab-runner status</code></pre>
<p>실행중이면 OK
프로젝트 &gt; Settings &gt; CI/CD에서 등록/활성 상태 확인 가능</p>
<p>파이프라인 실행 후 “pending”에서 “running”으로 변하면 정상입니다.</p>
<blockquote>
<p>재시작</p>
</blockquote>
<pre><code class="language-bash">sudo gitlab-runner restart</code></pre>
<h3 id="설치후기">설치후기</h3>
<pre><code class="language-bash"># 실제로 pending에서 running으로 바로 변경되었으면 좋겠지만 아래와 같은 오류가 발생함.
Running with gitlab-runner 18.5.0 (bda84871)
  on [토큰정보], system ID:
Preparing the &quot;shell&quot; executor
00:00
Using Shell (bash) executor...
Preparing environment
00:01
Running on [서버명]...
ERROR: Job failed: prepare environment: exit status 1. Check https://docs.gitlab.com/runner/shells/#shell-profile-loading for more information</code></pre>
<blockquote>
<p>해결방법</p>
</blockquote>
<pre><code class="language-bash">sudo rm /home/gitlab-runner/.bash_logout</code></pre>
<h3 id="-참고용-세팅">※ 참고용 세팅</h3>
<blockquote>
<p>❕기본적인 세팅이고, 어디까지나 참고용이며, 추후 테스트로직 등 보완해 나갈 예정이다.</p>
</blockquote>
<ul>
<li>nginx,php 는 docker 환경이고, node는 전역설치로 세팅된 환경</li>
</ul>
<blockquote>
<p>.gitlab-ci.yml</p>
</blockquote>
<pre><code class="language-yaml">stages:
  - deploy

deploy_dev:
  stage: deploy
  only:
    - master
  variables:
    GIT_STRATEGY: none  # GitLab Runner에서 코드를 clone하지 않음
  script:
    # 1. SSH 세팅
    - &#39;which ssh-agent || ( apt-get update -y &amp;&amp; apt-get install openssh-client -y )&#39;
    - eval $(ssh-agent -s)
    - echo &quot;$SSH_PRIVATE_KEY&quot; | tr -d &#39;\r&#39; | ssh-add -
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - ssh-keyscan -p [$SSH_PORT] -H [$SSH_IP] &gt;&gt; ~/.ssh/known_hosts

    # 2. 배포 스크립트를 서버에 생성하고 실행
    - |
      ssh -p [$SSH_PORT] [$SSH_ID]@[$SSH_IP] &#39;bash -s&#39; &lt;&lt; &#39;ENDSSH&#39;
      cd /var/www/[PROJECT ROOT PATH]

      # 배포 스크립트 생성
      cat &gt; /tmp/deploy_script.sh &lt;&lt; &#39;DEPLOY_SCRIPT&#39;
      #!/bin/bash
      echo &quot;======================================&quot;
      echo &quot;🚀 배포 시작...&quot;
      echo &quot;======================================&quot;
      cd /var/www/[PROJECT ROOT PATH]

      echo &quot;&quot;
      echo &quot;[1/6] 📥 최신 코드 가져오기...&quot;
      git pull origin master || { echo &quot;❌ Git pull 실패&quot;; exit 1; }
      echo &quot;✅ Git pull 성공&quot;

      echo &quot;&quot;
      echo &quot;[2/6] 📦 Composer 의존성 설치...&quot;
      docker compose exec -T php composer install --no-dev --optimize-autoloader --no-interaction 2&gt;&amp;1 | tail -20
      if [ ${PIPESTATUS[0]} -ne 0 ]; then
        echo &quot;❌ Composer 설치 실패&quot;
        exit 1
      fi
      echo &quot;✅ Composer 설치 성공&quot;

      echo &quot;&quot;
      echo &quot;[3/6] 📦 NPM 의존성 설치...&quot;
      npm install --production=false &gt; /tmp/npm_install.log 2&gt;&amp;1
      if [ $? -ne 0 ]; then
        echo &quot;❌ NPM 설치 실패&quot;
        tail -20 /tmp/npm_install.log
        exit 1
      fi
      echo &quot;✅ NPM 설치 성공&quot;

      echo &quot;&quot;
      echo &quot;[4/6] 🎨 프론트엔드 빌드...&quot;
      npm run build &gt; /tmp/npm_build.log 2&gt;&amp;1
      if [ $? -ne 0 ]; then
        echo &quot;❌ 빌드 실패&quot;
        tail -20 /tmp/npm_build.log
        exit 1
      fi
      echo &quot;✅ 빌드 성공&quot;

      echo &quot;&quot;
      echo &quot;[5/6] ⚡ 캐시 초기화&quot;
      docker compose exec -T php php artisan optimize:clear
      echo &quot;✅ 캐시 초기화 완료&quot;

      echo &quot;&quot;
      echo &quot;[6/6] 🗄️ 데이터베이스 마이그레이션...&quot;
      docker compose exec -T php php artisan migrate --force
      echo &quot;✅ 마이그레이션 완료&quot;

      echo &quot;&quot;
      echo &quot;======================================&quot;
      echo &quot;🎉 배포 완료!&quot;
      echo &quot;======================================&quot;
      DEPLOY_SCRIPT

      # 스크립트 실행
      chmod +x /tmp/deploy_script.sh
      /tmp/deploy_script.sh
      DEPLOY_EXIT=$?

      # 스크립트 삭제
      rm -f /tmp/deploy_script.sh /tmp/npm_install.log /tmp/npm_build.log

      exit $DEPLOY_EXIT
      ENDSSH

  # 배포 실패 시 알림 (선택사항)
  after_script:
    - echo &quot;배포 프로세스가 완료되었습니다.&quot;</code></pre>
<blockquote>
<p>※참고로 브랜치가 보호되어 있지 않으면 변수에 값이 들어가지 않는다!!
<img src="https://velog.velcdn.com/images/dev-bono/post/d9f22ec5-bcb2-4c2c-a848-487a0d7d553a/image.png" alt=""></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Nginx] SSR시 과도한 트레픽이 발생할 경우! 필수체크]]></title>
            <link>https://velog.io/@dev-bono/SSR%EC%8B%9C-%EA%B3%BC%EB%8F%84%ED%95%9C-%ED%8A%B8%EB%A0%88%ED%94%BD%EC%9D%B4-%EB%B0%9C%EC%83%9D%ED%95%A0-%EA%B2%BD%EC%9A%B0-%ED%95%84%EC%88%98%EC%B2%B4%ED%81%AC</link>
            <guid>https://velog.io/@dev-bono/SSR%EC%8B%9C-%EA%B3%BC%EB%8F%84%ED%95%9C-%ED%8A%B8%EB%A0%88%ED%94%BD%EC%9D%B4-%EB%B0%9C%EC%83%9D%ED%95%A0-%EA%B2%BD%EC%9A%B0-%ED%95%84%EC%88%98%EC%B2%B4%ED%81%AC</guid>
            <pubDate>Wed, 16 Apr 2025 09:40:23 GMT</pubDate>
            <description><![CDATA[<p>웹서버에서 gzip 설정 필수!</p>
<blockquote>
<p>/etc/nginx/nginx.conf</p>
</blockquote>
<pre><code class="language-bash">gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_vary on;</code></pre>
<blockquote>
<p>설명</p>
</blockquote>
<p>gzip on : 압축 사용
gzip_types ... : 어떤 응답을 압축할지
gzip_proxied any : 프록시 요청도 압축 허용
gzip_comp_level 6 :적당한 속도/압축률
gzip_min_length 1000 : 너무 짧은 건 압축 안 함
gzip_vary on : 캐시 서버와의 호환성 보장</p>
<blockquote>
<p>gzip 설정 전 / 후 트래픽 차이</p>
</blockquote>
<p>before : 3.1mb
after  : 92.2kb
<strong>※ 약 34.4배</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Nginx Reverse Proxy from Laravel]]></title>
            <link>https://velog.io/@dev-bono/Nginx-Reverse-Proxy-from-Laravel</link>
            <guid>https://velog.io/@dev-bono/Nginx-Reverse-Proxy-from-Laravel</guid>
            <pubDate>Mon, 23 Dec 2024 09:15:41 GMT</pubDate>
            <description><![CDATA[<h2 id="1-nginx-설정">1. Nginx 설정</h2>
<pre><code class="language-bash">server {
    listen 80;
    server_name example.com;

    # 내부 서비스의 포트로 프록시
    location / {
        # 연결될 서비스 IP:PORT 정보
        proxy_pass http://127.0.0.1:580;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # HTTPS 여부를 Laravel로 전달
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}</code></pre>
<h2 id="2-https-certbot-인증서-적용">2. Https (Certbot) 인증서 적용</h2>
<pre><code class="language-bash">sudo certbot --nginx -d example.com</code></pre>
<h2 id="3-라라벨-설정-변경">3. 라라벨 설정 변경</h2>
<pre><code class="language-bash">vi App\Http\Middleware\TrustProxies</code></pre>
<h3 id="--proxies의-값을-할당">- $proxies의 값을 할당</h3>
<pre><code class="language-php">namespace App\Http\Middleware;

use Illuminate\Http\Request;
use Fideloper\Proxy\TrustProxies as Middleware;

class TrustProxies extends Middleware
{
    /**
     * The trusted proxies for this application.
     *
     * @var array&lt;int, string|null&gt;
     */
    protected $proxies = &#39;*&#39;; // 또는 [&#39;127.0.0.1&#39;] (프록시 서버의 IP)

    /**
     * The headers that should be used to detect proxies.
     *
     * @var int
     */
    protected $headers = Request::HEADER_X_FORWARDED_ALL;
}</code></pre>
<blockquote>
<p>위 설정을 하지 않을 경우, 라라벨에서는 리버스프록시 설정시 모든파사드의 URL연결을 Http로 간주하게됨.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter 로컬API 호출]]></title>
            <link>https://velog.io/@dev-bono/Flutter-%EB%A1%9C%EC%BB%AC%ED%99%98%EA%B2%BD</link>
            <guid>https://velog.io/@dev-bono/Flutter-%EB%A1%9C%EC%BB%AC%ED%99%98%EA%B2%BD</guid>
            <pubDate>Wed, 23 Oct 2024 05:38:03 GMT</pubDate>
            <description><![CDATA[<h2 id="api-호출-예시">API 호출 예시</h2>
<ul>
<li>패키지 검색 방법<blockquote>
<p><a href="https://pub.dev">https://pub.dev</a></p>
</blockquote>
</li>
<li>패키지 추가<pre><code class="language-yml"># pubspec.yaml
dependencies:
  http: ^1.2.2</code></pre>
</li>
<li>실제 코드 반영<pre><code class="language-dart">import &#39;package:http/http.dart&#39; as http;
</code></pre>
</li>
</ul>
<p>Future<void> fetchData() async {
    // localhost 대신 10.0.2.2로 해야 로컬 API를 가져올 수 있다. 
    final url = Uri.http(&#39;10.0.2.2&#39;, &#39;/products/&#39;);
    // final url = Uri.https(&#39;test.com&#39;, &#39;/products/&#39;);
    final response = await http.get(url);
    if (response.statusCode == 200) {
      setState(() {
        _data = json.decode(convert.utf8.decode(response.bodyBytes));
      });
    } else {
      print(&#39;Failed to load data: ${response.statusCode}&#39;);
    }
  }</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[명령어]]></title>
            <link>https://velog.io/@dev-bono/%EB%AA%85%EB%A0%B9%EC%96%B4</link>
            <guid>https://velog.io/@dev-bono/%EB%AA%85%EB%A0%B9%EC%96%B4</guid>
            <pubDate>Tue, 06 Aug 2024 05:59:15 GMT</pubDate>
            <description><![CDATA[<h2 id="geth-ipcinterprocess-communication">geth IPC(InterProcess Communication)</h2>
<pre><code class="language-bash">geth attach [geth.ipc파일 경로]
------------geth 진입------------
&gt; eth.syncing   
# 동기화 중일때 해당 객체를 반환한다.
{
  currentBlock: 7859139,
  healedBytecodeBytes: 0,
  healedBytecodes: 0,
  healedTrienodeBytes: 0,
  healedTrienodes: 0,
  healingBytecode: 0,
  healingTrienodes: 0,
  highestBlock: 20467446,
  startingBlock: 7819980,
  syncedAccountBytes: 37302563242,
  syncedAccounts: 180074829,
  syncedBytecodeBytes: 6263066910,
  syncedBytecodes: 918816,
  syncedStorage: 798688668,
  syncedStorageBytes: 178108201913,
  txIndexFinishedBlocks: 0,
  txIndexRemainingBlocks: 1
}
# 동기화 중이지 않을 경우 &quot;false&quot;를 반환한다.
false

# 1차 목표는 currentBlock과 highestBlock이 같은값(동기화) 되어야 한다.
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[시스템 서비스로 관리하기]]></title>
            <link>https://velog.io/@dev-bono/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%9C%EB%B9%84%EC%8A%A4%EB%A1%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dev-bono/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%9C%EB%B9%84%EC%8A%A4%EB%A1%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 06 Aug 2024 01:30:13 GMT</pubDate>
            <description><![CDATA[<p>systemd를 사용하는 시스템에서는 Geth를 서비스로 설정하여 실행할 수 있습니다. 이 방법이 가장 안정적이고 관리하기 쉽습니다.
a. <code>/etc/systemd/system/geth.service</code> 파일을 생성합니다.(최소 용량을 위해 snap syncmode로 진행)</p>
<pre><code class="language-bash">sudo vi /etc/systemd/system/geth.service</code></pre>
<pre><code class="language-bash">[Unit]
Description=Ethereum go client
After=network.target
Wants=network.target

[Service]
User=ethereum  
Group=ethereum
WorkingDirectory=/home/ethereum
ExecStart=/usr/bin/geth --datadir /var/lib/ethereum \
  --authrpc.addr localhost \
  --authrpc.port 8551 \
  --authrpc.vhosts localhost \
  --authrpc.jwtsecret /var/lib/ethereum/geth/jwtsecret \
  --http \
  --http.api eth,net,engine,admin \
  --http.corsdomain &quot;*&quot; \
  --syncmode snap 
Restart=on-failure
RestartSec=10s

[Install]
WantedBy=multi-user.target</code></pre>
<p>b. <code>/etc/systemd/system/prysm.service</code> 파일을 생성합니다.</p>
<pre><code class="language-bash">sudo vi /etc/systemd/system/prysm.service</code></pre>
<pre><code class="language-bash">[Unit]
Description=Prysm Ethereum 2.0 beacon chain
After=network.target geth.service
Wants=network.target geth.service

[Service]
User=ethereum
Group=ethereum
WorkingDirectory=/home/ethereum
ExecStart=/var/lib/ethereum/prysm.sh beacon-chain \
  --execution-endpoint=http://localhost:8551 \
  --mainnet \
  --jwt-secret=/var/lib/ethereum/geth/jwtsecret \
  --checkpoint-sync-url=https://beaconstate.info \
  --genesis-beacon-api-url=https://beaconstate.info
Restart=on-failure
RestartSec=10s

[Install]
WantedBy=multi-user.target</code></pre>
<p>c. 유저생성(없을시)</p>
<pre><code class="language-bash"># 유저생성
# -r: 이 옵션은 시스템 계정(system account)을 생성한다는 의미입니다. 시스템 계정은 일반적으로 서비스를 실행하기 위해 사용되며, 일반 사용자 계정과는 몇 가지 차이가 있습니다:
# -s : 이 옵션은 사용자의 로그인 셸을 지정합니다. 
sudo useradd -r -s /bin/bash ethereum
또는
sudo useradd -r -s /sbin/nologin ethereum 
# /sbin/nologin 유저의 경우 쉘로 접근할 수 없기 때문에 /bin/bash로 실제 명령이 잘 수행되는지 테스트 한 뒤 생성을 권장.

sudo mkdir -p /home/ethereum
sudo chown ethereum:ethereum /home/ethereum
# 디렉토리 소유권한 변경
sudo chown -R ethereum:ethereum /var/lib/ethereum
# 권한 변경(권한755시 권한오류가 발생)
sudo chmod -R 777 /var/lib/ethereum
sudo chmod 755 /usr/bin/geth</code></pre>
<p>d. 서비스를 시작합니다:</p>
<pre><code class="language-bash"># 새로운 서비스 등록
sudo systemctl daemon-reload
# 서비스 시작
sudo systemctl start geth
sudo systemctl start prysm
# 부팅시 자동시작 활성화
sudo systemctl enable geth
sudo systemctl enable prysm</code></pre>
<p>e. 실시간 로그확인</p>
<pre><code class="language-bash">sudo journalctl -f -u geth.service
sudo journalctl -f -u prysm.service</code></pre>
<p>이 방법을 사용하면 시스템 재부팅 시에도 자동으로 Geth,Prysm이 시작되며, 로그 관리와 모니터링이 더 쉬워집니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Consensus Clients(합의 클라이언트)]]></title>
            <link>https://velog.io/@dev-bono/Consensus-Clients%ED%95%A9%EC%9D%98-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8</link>
            <guid>https://velog.io/@dev-bono/Consensus-Clients%ED%95%A9%EC%9D%98-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8</guid>
            <pubDate>Mon, 05 Aug 2024 10:11:22 GMT</pubDate>
            <description><![CDATA[<h2 id="prysm-설치">Prysm 설치</h2>
<pre><code class="language-bash"># 기본 디렉토리 구성
mkdir -p /var/lib/ethereum/consensus
mkdir -p /var/lib/ethereum/execution
cd /var/lib/ethereum
# Prysm 클라이언트가 다운로드되고 실행 가능하게 됩니다.
sudo curl https://raw.githubusercontent.com/prysmaticlabs/prysm/master/prysm.sh --output /var/lib/ethereum/prysm.sh
sudo chmod +x /var/lib/ethereum/prysm.sh
sudo ./prysm.sh beacon-chain generate-auth-secret
# 실행 클라이언트 실행
sudo geth --mainnet --http --http.api eth,net,engine,admin --authrpc.jwtsecret=/var/lib/ethereum/geth/jwtsecret

#비콘노드 실행
sudo ./prysm.sh beacon-chain --execution-endpoint=http://localhost:8551 --mainnet --jwt-secret=/var/lib/ethereum/geth/jwtsecret --checkpoint-sync-url=https://beaconstate.info --genesis-beacon-api-url=https://beaconstate.info


</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[ Execution Client(실행 클라이언트)]]></title>
            <link>https://velog.io/@dev-bono/Execution-Client%EC%8B%A4%ED%96%89-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8</link>
            <guid>https://velog.io/@dev-bono/Execution-Client%EC%8B%A4%ED%96%89-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8</guid>
            <pubDate>Mon, 05 Aug 2024 09:44:54 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-bash"># --authrpc.addr localhost: 인증된 RPC 인터페이스를 localhost에 바인딩합니다.
# --authrpc.port 8551: 인증된 RPC 인터페이스가 사용할 포트를 8551로 설정합니다.
# --authrpc.vhosts localhost: localhost에서의 접근만 허용합니다.
# --authrpc.jwtsecret /path/to/jwtsecret: JWT 시크릿 파일의 경로를 지정합니다.
# --datadir /path/to/ethereum-data : 이더리움 데이터가 저장될 경로를 지정합니다.
# --http --http.api eth,net,engine,admin : HTTP RPC를 활성화하고 필요한 API를 노출합니다.
# --syncmode snap : 빠른 동기화 모드를 사용합니다.
geth --authrpc.addr localhost --authrpc.port 8551 --authrpc.vhosts localhost --authrpc.jwtsecret /path/to/jwtsecret --datadir /path/to/ethereum-data --http --http.api eth,net,engine,admin --syncmode snap

# 예시
mkdir -p /var/lib/ethereum/geth
# 실행 클라이언트 실행
sudo geth --datadir /var/lib/ethereum \
     --authrpc.addr localhost \
     --authrpc.port 8551 \
     --authrpc.vhosts localhost \
     --authrpc.jwtsecret /var/lib/ethereum/geth/jwtsecret \
     --http --http.api eth,net,engine,admin</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Go-Ethereum 설치]]></title>
            <link>https://velog.io/@dev-bono/Go-Ethereum-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@dev-bono/Go-Ethereum-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Mon, 05 Aug 2024 08:06:10 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>설치환경 : ubuntu24.04</p>
</blockquote>
<pre><code class="language-bash"># software-properties-common 패키지 설치 
sudo apt install software-properties-common  
# 이더리움PPA 추가
sudo add-apt-repository -y ppa:ethereum/ethereum
# PPA에 추가된 내용 업데이트
sudo apt update
# 이더리움 설치
sudo apt install ethereum
# 버전확인
geth version</code></pre>
<p><img src="https://velog.velcdn.com/images/dev-bono/post/f533d953-ea11-40e7-b539-45777180368d/image.png" alt=""></p>
<p>참조 : <a href="https://geth.ethereum.org/docs/getting-started/installing-geth">https://geth.ethereum.org/docs/getting-started/installing-geth</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[하이퍼레저 패브릭 오류]]></title>
            <link>https://velog.io/@dev-bono/%ED%95%98%EC%9D%B4%ED%8D%BC%EB%A0%88%EC%A0%80%ED%8C%A8%EB%B8%8C%EB%A6%AD-%EC%98%A4%EB%A5%98</link>
            <guid>https://velog.io/@dev-bono/%ED%95%98%EC%9D%B4%ED%8D%BC%EB%A0%88%EC%A0%80%ED%8C%A8%EB%B8%8C%EB%A6%AD-%EC%98%A4%EB%A5%98</guid>
            <pubDate>Wed, 10 Jul 2024 01:26:33 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>./network.sh: bad interpreter: /bin/bash^M: no such file or directory</strong></p>
</blockquote>
<p>이 오류는 일반적으로 Windows 시스템에서 파일을 편집하거나 생성한 후 Linux 시스템(예: WSL)에서 실행할 때 발생합니다. 문제의 원인은 줄 끝 문자(line ending) 차이 때문입니다. Windows는 CRLF(Carriage Return + Line Feed, \r\n)를 사용하고, Linux는 LF(Line Feed, \n)만 사용합니다.</p>
<p>이 문제를 해결하기 위해 다음 단계를 따라해 보세요:</p>
<ol>
<li><p>dos2unix 유틸리티 설치:</p>
<pre><code class="language-bash">sudo apt-get update
sudo apt-get install dos2unix</code></pre>
</li>
<li><p>network.sh 파일의 줄 끝 문자 변환:</p>
<pre><code class="language-bash">dos2unix network.sh</code></pre>
</li>
<li><p>파일 실행 권한 확인 및 부여:</p>
<pre><code class="language-bash">chmod +x network.sh</code></pre>
</li>
<li><p>다시 스크립트 실행:</p>
<pre><code class="language-bash">./network.sh up</code></pre>
</li>
</ol>
<p>만약 dos2unix를 사용할 수 없는 경우, 다음의 sed 명령어를 사용할 수 있습니다:</p>
<pre><code class="language-bash">sed -i &#39;s/\r$//&#39; network.sh</code></pre>
<p>이 명령어는 파일에서 모든 캐리지 리턴(\r) 문자를 제거합니다.</p>
<p>또는 Vi나 Vim 편집기를 사용중이라면, 다음 명령으로 파일 형식을 변경할 수 있습니다:</p>
<ol>
<li><p>Vi나 Vim으로 파일 열기:</p>
<pre><code class="language-bash">vi network.sh</code></pre>
</li>
<li><p>명령 모드에서 다음 명령 실행:</p>
<pre><code>:set fileformat=unix
:wq</code></pre></li>
</ol>
<p>이 과정을 거친 후에는 스크립트가 정상적으로 실행될 것입니다. 여전히 문제가 발생한다면 알려주세요.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[하이퍼레저 패브릭]]></title>
            <link>https://velog.io/@dev-bono/%ED%95%98%EC%9D%B4%ED%8D%BC%EB%A0%88%EC%A0%80-%ED%8C%A8%EB%B8%8C%EB%A6%AD</link>
            <guid>https://velog.io/@dev-bono/%ED%95%98%EC%9D%B4%ED%8D%BC%EB%A0%88%EC%A0%80-%ED%8C%A8%EB%B8%8C%EB%A6%AD</guid>
            <pubDate>Tue, 09 Jul 2024 09:56:10 GMT</pubDate>
            <description><![CDATA[<h1 id="ubuntu2404">ubuntu24.04</h1>
<ol>
<li><p>시스템 업데이트 및 필수 도구 설치:</p>
<pre><code class="language-bash">sudo apt update
sudo apt install -y git curl wget software-properties-common</code></pre>
</li>
<li><p>Python 설치:</p>
<pre><code class="language-bash">sudo apt install -y python3 python3-pip
python3 --version
pip3 --version</code></pre>
</li>
<li><p>Go 설치:</p>
<pre><code class="language-bash">wget https://golang.org/dl/go1.18.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.18.linux-amd64.tar.gz
echo &#39;export PATH=$PATH:/usr/local/go/bin&#39; &gt;&gt; ~/.bashrc
source ~/.bashrc
go version</code></pre>
</li>
<li><p>Docker 및 Docker Compose 설치:</p>
<pre><code class="language-bash">sudo apt install -y docker.io docker-compose
sudo usermod -aG docker $USER</code></pre>
<p>(Docker 권한 변경 후 로그아웃 후 다시 로그인하세요)</p>
</li>
<li><p>Node.js와 npm 설치:</p>
<pre><code class="language-bash">curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install -y nodejs</code></pre>
</li>
<li><p>하이퍼레저 패브릭 샘플 및 바이너리 다운로드:</p>
<pre><code class="language-bash">curl -sSL https://bit.ly/2ysbOFE | bash -s -- 2.5.0 1.5.5</code></pre>
</li>
<li><p>환경 변수 설정:</p>
<pre><code class="language-bash">echo &#39;export PATH=$PATH:$HOME/fabric-samples/bin&#39; &gt;&gt; ~/.bashrc
source ~/.bashrc</code></pre>
</li>
<li><p>test-network 디렉토리로 이동:</p>
<pre><code class="language-bash">cd fabric-samples/test-network</code></pre>
</li>
<li><p>Docker Compose 파일 생성:
<code>docker-compose.yaml</code> 파일을 다음 내용으로 생성합니다:</p>
</li>
</ol>
<pre><code class="language-yaml">version: &#39;2&#39;

networks:
  basic:

services:
  ca_org1:
    image: hyperledger/fabric-ca:1.5.5
    environment:
      - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server
      - FABRIC_CA_SERVER_CA_NAME=ca-org1
      - FABRIC_CA_SERVER_TLS_ENABLED=true
      - FABRIC_CA_SERVER_PORT=7054
    ports:
      - &quot;7054:7054&quot;
    command: sh -c &#39;fabric-ca-server start -b admin:adminpw -d&#39;
    volumes:
      - ./organizations/fabric-ca/org1:/etc/hyperledger/fabric-ca-server
    container_name: ca_org1
    networks:
      - basic

  ca_org2:
    image: hyperledger/fabric-ca:1.5.5
    environment:
      - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server
      - FABRIC_CA_SERVER_CA_NAME=ca-org2
      - FABRIC_CA_SERVER_TLS_ENABLED=true
      - FABRIC_CA_SERVER_PORT=8054
    ports:
      - &quot;8054:8054&quot;
    command: sh -c &#39;fabric-ca-server start -b admin:adminpw -d&#39;
    volumes:
      - ./organizations/fabric-ca/org2:/etc/hyperledger/fabric-ca-server
    container_name: ca_org2
    networks:
      - basic

  ca_orderer:
    image: hyperledger/fabric-ca:1.5.5
    environment:
      - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server
      - FABRIC_CA_SERVER_CA_NAME=ca-orderer
      - FABRIC_CA_SERVER_TLS_ENABLED=true
      - FABRIC_CA_SERVER_PORT=9054
    ports:
      - &quot;9054:9054&quot;
    command: sh -c &#39;fabric-ca-server start -b admin:adminpw -d&#39;
    volumes:
      - ./organizations/fabric-ca/ordererOrg:/etc/hyperledger/fabric-ca-server
    container_name: ca_orderer
    networks:
      - basic</code></pre>
<ol start="10">
<li><p>Docker Compose로 네트워크 시작:</p>
<pre><code class="language-bash">docker-compose up -d</code></pre>
</li>
<li><p>네트워크 상태 확인:</p>
<pre><code class="language-bash">docker-compose ps</code></pre>
</li>
<li><p>Python 및 Go용 하이퍼레저 패브릭 SDK 설치:</p>
</li>
</ol>
<p>Python SDK:</p>
<pre><code class="language-bash">pip3 install fabric-sdk-py</code></pre>
<p>Go SDK:</p>
<pre><code class="language-bash">go get -u github.com/hyperledger/fabric-sdk-go</code></pre>
<ol start="13">
<li>네트워크 종료 (필요시):<pre><code class="language-bash">docker-compose down</code></pre>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Nginx] 중규모 이상 서비스 설정]]></title>
            <link>https://velog.io/@dev-bono/Nginx-%EC%A4%91%EA%B7%9C%EB%AA%A8-%EC%9D%B4%EC%83%81-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@dev-bono/Nginx-%EC%A4%91%EA%B7%9C%EB%AA%A8-%EC%9D%B4%EC%83%81-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Mon, 23 Oct 2023 03:44:17 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dev-bono/post/59233808-1bf7-4da4-83d1-b61127f65822/image.png" alt=""></p>
<blockquote>
<p> 768 worker_connections are not enough</p>
</blockquote>
<p>위 오류는 Ubuntu 22.04 LTS 버전으로 nginx 를 설치시 기본 워커의 연결가능한 개수가 768개로 지정되어 있기 때문에 워커에 연결되는 수가 많아 질 경우 발생하는 오류이다. </p>
<p>필자는 아래와 같은 방법으로 해결했으며, 서비스와 PC스펙에 따라서 처리하는 방법은 달라질 수 있겠다.</p>
<h1 id="서버환경">서버환경</h1>
<ul>
<li>Ubuntu 22.04 LTS</li>
<li>Nginx 1.18.0</li>
<li>VM서버 24vCPU / 64G Memory</li>
</ul>
<h1 id="처리과정">처리과정</h1>
<h2 id="1-nginx-워커의-커넥션-최대치-수정">1. Nginx 워커의 커넥션 최대치 수정</h2>
<pre><code class="language-bash">sudo vi /etc/nginx/nginx.conf

events {
        # 기존 768이라고 되어있던 부분을 수정해준다.
        # worker_connections 768;
        worker_connections 2048;
}

sudo service nginx restart</code></pre>
<h2 id="2-fdfile-descriptor-설정-변경">2. FD(File Descriptor) 설정 변경</h2>
<p>설정 방법은 크게  세가지로 나뉜다.</p>
<ul>
<li>OS 설정 변경(적용함)</li>
<li>H/W 설정 변경(적용함)</li>
<li>Nginx 설정 변경</li>
</ul>
<blockquote>
<p><strong>OS 설정 변경</strong></p>
</blockquote>
<p>아래 부분을 추가 후 저장한다. 현재 서비스에는 * 로 지정을 해 두었다.</p>
<pre><code class="language-bash">sudo vi /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535
# 또는
www-data soft nofile 65535
www-data hard nofile 65535

-- 위와 같이 하니 서버가 버티질 못하였다.

nginx       soft    nofile  50000
nginx       hard    nofile  80000
root       soft    nofile  unlimited
root       hard    nofile  unlimited
www-data    soft    nofile  unlimited
www-data    hard    nofile  unlimited

-- 위와 같이 다시 설정을 다시 진행 함.

# 세션초기화
exec bash 
# 또는
exec zsh</code></pre>
<blockquote>
<p><strong>PHP-FPM 설정 파일 변경</strong></p>
</blockquote>
<p> [error] 1170#1170: *4604237 connect() to unix:/run/php/php8.1-fpm.sock failed (11: Resource temporarily unavailable) while connecting to upstream</p>
<p><strong>위와 같은 오류가 어느날 발생하기 시작함.</strong></p>
<p>pm.max_children 수정 : <a href="http://www.conf">www.conf</a> 파일에서 pm.max_children 값을 조정하여 동시에 처리할 수 있는 PHP-FPM 프로세스의 수를 늘립니다.</p>
<pre><code class="language-bash">vi /etc/php/8.1/fpm/pool.d/www.conf

# 상태 분류 : static(정적), dynamic(동적), ondemand(요청에 따른) 
pm = static #static은 최대치로 자동 설정 
pm.max_children = 50

# pm.max_children 는 현재 운영중인 서버의 여유메모리와, FPM의 평균메모리에 여유분을 5~6Mb 더 주고 나눠서 계산한다.
# 평균메모리 계산
# ps --no-headers -o &quot;rss,cmd&quot; -C php-fpm8.1 | awk &#39;{ sum+=$1 } END { printf (&quot;%d%sn&quot;, sum/NR/1024,&quot;M&quot;) }&#39;</code></pre>
<blockquote>
<p><strong>H/W 설정 변경</strong></p>
</blockquote>
<p>해당 부분은 현재 서비스되고 있는 환경에 맞게 스팩을 늘이면 될 것이다. 
필자의 서버는 현재 VM(Hyper-V)서버로 운영중이며, 동시에 여러 서비스가 운영될 예정임으로 넋넋하게 세팅을 해 두었다.</p>
<blockquote>
<p><strong>Nginx 설정 변경</strong></p>
</blockquote>
<p>nginx 설정에서 <code>worker_rlimit_nofile</code> 를 수정해서 프로세스가 가질 수 있는 FD수를 제한 할 수 있습니다.
위 방법은 도입해 보지 않았으므로, 이 글에서는 다루지 않기로 하겠습니다.</p>
<h1 id="후기">후기</h1>
<ul>
<li>현재 운영중인 서비스가 점점 커지는 추세이고, 워커에 연결되는 커넥션은 점점 늘어날 전망이다. 현재 세팅이 어디까지 장애 없이 서비스가 될 지 모르겠으나, 앞으로 추가적인 장애가 발생 시 추가로 글을 작성할 예정이다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Laravel] 패키지 모음💻]]></title>
            <link>https://velog.io/@dev-bono/Laravel-%ED%8C%A8%ED%82%A4%EC%A7%80-%EB%AA%A8%EC%9D%8C</link>
            <guid>https://velog.io/@dev-bono/Laravel-%ED%8C%A8%ED%82%A4%EC%A7%80-%EB%AA%A8%EC%9D%8C</guid>
            <pubDate>Sat, 16 Sep 2023 12:27:11 GMT</pubDate>
            <description><![CDATA[<h1 id="패키지분류">패키지분류</h1>
<h2 id="ⅰ-테스트--디버깅">Ⅰ. 테스트 / 디버깅</h2>
<pre><code class="language-php"># 디버그바( .env &gt; APP_DEBUG=true 일 경우 자동 enabled )
composer require barryvdh/laravel-debugbar --dev
...
</code></pre>
<h2 id="ⅱ-인증">Ⅱ. 인증</h2>
<pre><code class="language-php"># Continue...</code></pre>
<h2 id="ⅲ-ui--ux">Ⅲ. UI / UX</h2>
<pre><code class="language-php"># Continue...</code></pre>
<h1 id="후기">후기</h1>
<blockquote>
<p>Continue...</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Laravel] Laravel10 Starter😁]]></title>
            <link>https://velog.io/@dev-bono/Laravel10-Starter</link>
            <guid>https://velog.io/@dev-bono/Laravel10-Starter</guid>
            <pubDate>Wed, 13 Sep 2023 13:45:27 GMT</pubDate>
            <description><![CDATA[<h2 id="개발환경">개발환경</h2>
<blockquote>
<p><strong>Windows11 | WSL2(Ubuntu22.04) | Docker</strong>
<strong>라라벨 버전 : 10.x.x(Latest)</strong>
<strong>설치 방법 : Sail</strong></p>
</blockquote>
<h2 id="설치-과정">설치 과정</h2>
<h3 id="ⅰ-다운로드-및-빌드">Ⅰ. 다운로드 및 빌드</h3>
<ul>
<li>Laravel Sail - <a href="https://laravel.com/docs/10.x#choosing-your-sail-services">https://laravel.com/docs/10.x#choosing-your-sail-services</a></li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-bono/post/021f674f-b41d-4ebb-83be-41f4e8ed43fd/image.png" alt=""></p>
<pre><code class="language-bash"># 프로젝트명을 원하는 프로젝트명으로 변경 후 
# with 에는 도커 컨테이너에 추가로 포함시킬 이미지를 선택하며,
# 콤마로 구분하며 사용할 이미지만 넣어서 설치해 준다.
# devcontainer옵션은 도커 컨테이너 내부에서 개발할 경우 붙여주지만 여기서는 사용하지 않습니다.
curl -s &quot;https://laravel.build/{프로젝트명}?with=mysql,redis&quot; | bash</code></pre>
<blockquote>
<p>첫 설치시 리소스(도커 이미지와 라라벨소스)를 내려받고 빌드하는 과정이 매우 길다.😴</p>
</blockquote>
<h3 id="ⅱ-도커-컨테이너-실행">Ⅱ. 도커 컨테이너 실행</h3>
<pre><code class="language-bash">cd {프로젝트명}
# 내려받은 이미지를 도커 컨테이너로 등록
vendor/bin/sail up
# 혹은 백그라운드 실행 (down 명령으로 종료 )
vendor/bin/sail up -d </code></pre>
<blockquote>
<p>위 과정에서 로컬포트 충돌이 날 경우 <strong><em>docker-compose.yml</em></strong> 을 열어보면 해당 포트에 변수로 지정이 되어 있으며, 콜론 뒤 Default 로 선언된 포트를 변경 혹은 .env파일에서 환경변수를 추가하여 변경(권장) 하는 방법을 택하면 됩니다.</p>
</blockquote>
<h3 id="ⅲ-sail-별칭-설정">Ⅲ. Sail 별칭 설정</h3>
<pre><code class="language-bash"># vendor/bin/sail 로 명령어를 사용하긴 너무 비효율 적이니 별칭을 주기로 함
cd ~
# 자신의 쉘 환경에 맞게 rc파일을 수정 (아래는 zsh 기준)  
vi .zshrc    
# 별칭 추가
alias sail=&quot;vendor/bin/sail&quot;
:wq

# 수정된 소스 반영
source .zshrc

#프로젝트 경로에서 vendor/bin/sail이라고 하던걸 sail이라고만 하면 되게끔 수정되었다.
sail restart</code></pre>
<h2 id="설치-후기">설치 후기</h2>
<blockquote>
<p>라라벨 Sail은 라라벨을 로컬환경에서 쉽게 사용 할 수 있도록 한다.
설치 이후 사용법은 <strong>docker-compose</strong>와 유사하나, 일부 명령어를 생략해서 사용 할 수 있게끔 편의적인 부분이 보인다. 명령어를 보낼때 exec를 사용하는 부분이 불필요 하다거나, php artisan을 사용할때도 php를 생각 가능하다거나, 개발자에게 주는 피로감을 덜어주기 위해 설계된 디테일한 부분이 마음에 든다.</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>