<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ihopenre-eng.log</title>
        <link>https://velog.io/</link>
        <description>red teaming 하고싶어요</description>
        <lastBuildDate>Wed, 22 Jul 2026 05:03:16 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ihopenre-eng.log</title>
            <url>https://velog.velcdn.com/images/ihopenre-eng/profile/c0d30437-9b31-4d02-a459-7f543c77253a/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ihopenre-eng.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ihopenre-eng" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[GitLab CLI 기여기]]></title>
            <link>https://velog.io/@ihopenre-eng/deprecated-%ED%94%8C%EB%9E%98%EA%B7%B8%EB%A5%BC-%EB%8F%84%EC%9B%80%EB%A7%90%EC%9D%B4-%EA%B3%84%EC%86%8D-%EA%B6%8C%ED%95%98%EA%B3%A0-%EC%9E%88%EC%97%88%EB%8B%A4-GitLab-CLI-%EA%B8%B0%EC%97%AC%EA%B8%B0</link>
            <guid>https://velog.io/@ihopenre-eng/deprecated-%ED%94%8C%EB%9E%98%EA%B7%B8%EB%A5%BC-%EB%8F%84%EC%9B%80%EB%A7%90%EC%9D%B4-%EA%B3%84%EC%86%8D-%EA%B6%8C%ED%95%98%EA%B3%A0-%EC%9E%88%EC%97%88%EB%8B%A4-GitLab-CLI-%EA%B8%B0%EC%97%AC%EA%B8%B0</guid>
            <pubDate>Wed, 22 Jul 2026 05:03:16 GMT</pubDate>
            <description><![CDATA[<p>GitLab CLI(<code>glab</code>)에 기여할 거리를 찾다가 네 건을 찾았다.</p>
<p>기법 자체가 재현 가능해서 정리해본다.</p>
<h2 id="1-배열인데-문자열로-전송되는-파라미터">1. 배열인데 문자열로 전송되는 파라미터</h2>
<p><code>glab api</code>는 <code>-F &quot;key=[a, b]&quot;</code> 형태로 배열을 보낼 수 있다. 판별은 정규식이 한다.</p>
<pre><code>^\[\s*([[:lower:]_]+(\s*,\s*[[:lower:]_]+)*)?\s*\]$</code></pre><p>원소가 소문자와 밑줄로만 이루어져야 매칭된다.</p>
<pre><code class="language-bash">glab api -X PUT projects/:id -F &quot;topics=[my-topic, GitLab]&quot;
# 실제 전송: {&quot;topics&quot;:&quot;[my-topic, GitLab]&quot;}   ← 문자열
# 기대:     {&quot;topics&quot;:[&quot;my-topic&quot;,&quot;GitLab&quot;]}</code></pre>
<p>하이픈 하나, 대문자 하나면 통째로 문자열이 된다. 프로젝트 topics는 사용자가 직접 쓰는 값이라 하이픈과 대문자가 흔하다.</p>
<p>원소를 &quot;따옴표 문자열이거나, 리스트 문법과 혼동될 수 없는 문자열&quot;로 넓히고, 따옴표 밖의 콤마만 구분자로 처리하도록 고쳤다. <code>[&quot;one, two&quot;, three]</code>가 세 개가 아니라 두 개가 된다.</p>
<p>덤으로 <code>[]</code>가 빈 문자열 하나를 담은 배열로 변환되던 것도 잡았다. 필드를 비우려면 빈 배열을 보내야 하는데 그게 안 되고 있었다.</p>
<p><code>[</code>로 시작하면 전부 JSON으로 파싱하는 방법도 있었지만 택하지 않았다. <code>[1, 2]</code>가 문자열에서 숫자로 바뀌어 기존 사용자에게 영향을 준다. 이슈 범위를 넘는 결정이라 따로 제안하는 게 맞다고 봤다.</p>
<h2 id="2-버그-리포트-템플릿이-스스로-경고를-유발한다">2. 버그 리포트 템플릿이 스스로 경고를 유발한다</h2>
<p>환경변수 처리에 이런 코드가 있다.</p>
<pre><code class="language-go">if !strings.HasPrefix(key, &quot;GLAB_&quot;) {
    PrintDeprecationWarning(key)
}</code></pre>
<p><code>GLAB_</code> 접두어가 없으면 경고를 찍는다. 그런데 저장소의 버그 리포트 템플릿은 제보자에게 이렇게 안내하고 있었다.</p>
<pre><code>Run the command in debug mode (like `DEBUG=true glab mr list`)</code></pre><p>템플릿을 따르면 프로젝트 자신이 만든 deprecation 경고가 출력된다. 도움말과 공식 문서는 이미 <code>GLAB_DEBUG</code>만 안내하고 있었으니 뒤처진 건 템플릿 쪽이었다.</p>
<h2 id="3-도움말이-자기가-없앤-플래그를-권하는-문제">3. 도움말이 자기가 없앤 플래그를 권하는 문제</h2>
<p>이건 순전히 기계적으로 찾았다. <code>MarkDeprecated</code> 호출을 전부 모은 다음, <strong>그 플래그를 선언한 파일의 도움말 텍스트에 같은 플래그가 남아 있는지</strong> 대조했다.</p>
<pre><code>grep MarkDeprecated → 플래그 목록
→ 같은 파일의 Example/Long 텍스트에서 그 플래그 검색</code></pre><p>두 건이 걸렸다.</p>
<p><code>glab issue list</code>의 예시 마지막 줄:</p>
<pre><code class="language-bash">glab issue list --milestone release-2.0.0 --opened</code></pre>
<p>같은 파일 아래쪽:</p>
<pre><code class="language-go">_ = issueListCmd.Flags().MarkHidden(&quot;opened&quot;)
_ = issueListCmd.Flags().MarkDeprecated(&quot;opened&quot;, &quot;default if --closed is not used.&quot;)</code></pre>
<p><code>--opened</code>는 <code>--help</code>에 나오지도 않는다. 예시를 복사해 실행하면 경고가 뜨고, open이 이미 기본값이라 동작은 아무것도 안 바뀐다.</p>
<p><code>glab mr note</code>의 설명은 더 아쉬웠다.</p>
<blockquote>
<p>Creates a comment by default. Use <code>--resolve</code> or <code>--unresolve</code> to manage existing discussion threads.</p>
</blockquote>
<p>두 플래그 모두 deprecated이고 대체제는 <code>glab mr note resolve</code>와 <code>glab mr note reopen</code> 서브커맨드인데, 설명에는 그 서브커맨드가 아예 없었다.</p>
<p>사람은 플래그 선언부와 도움말 텍스트를 동시에 보지 않는다. 그래서 리뷰에서 안 걸린다. <code>grep</code> 대조 한 번이면 나온다.</p>
<h2 id="4-테스트가-저장소와-홈-디렉터리에-파일을-쓴다">4. 테스트가 저장소와 홈 디렉터리에 파일을 쓴다</h2>
<p>이건 의도치 않게 발견했다. Windows에서 <code>go test ./...</code>를 돌린 뒤 <code>git status</code>를 보니 못 보던 디렉터리가 생겨 있었다.</p>
<pre><code>?? internal/commands/ci/artifact/Users&lt;name&gt;AppDataLocalTempTest_NewCmdRun.../</code></pre><p>디렉터리 이름에서 백슬래시만 사라진 형태다. 원인은 테스트 헬퍼였다.</p>
<pre><code class="language-go">return func(cli string) (*test.CmdOut, error) {
    argv, err := shlex.Split(cli)</code></pre>
<p>커맨드라인을 문자열로 받아 <code>shlex.Split</code>으로 쪼갠다. shlex는 백슬래시를 이스케이프 문자로 취급한다. 그래서 <code>t.TempDir()</code>이 돌려준 <code>C:\Users\...\Temp\...</code>가 커맨드에는 <code>C:UsersAppDataLocalTemp...</code>라는 <strong>상대경로</strong>로 도착하고, 결과물이 패키지 디렉터리 안에 쌓인다. 당연히 테스트도 실패한다.</p>
<p><code>filepath.ToSlash</code>를 한 번 통과시키면 절대경로가 유지된다. 다른 OS에서는 아무 일도 하지 않는다.</p>
<p>더 나쁜 게 하나 더 있었다.</p>
<pre><code class="language-go">home := t.TempDir()
t.Setenv(&quot;HOME&quot;, home)</code></pre>
<p>Windows에서 <code>os.UserHomeDir</code>는 <code>HOME</code>이 아니라 <code>USERPROFILE</code>을 읽는다. 그래서 이 테스트는 임시 디렉터리가 아니라 <strong>실제 홈 디렉터리</strong>에 파일을 쓴다. 테스트를 한 번 돌리고 나면 홈 디렉터리에 <code>~/.agents/skills/glab/SKILL.md</code>가 실제로 생긴다.</p>
<p>고치는 방법은 이미 같은 저장소 안에 있었다.</p>
<pre><code class="language-go">// Set both HOME (Unix/macOS) and USERPROFILE (Windows) for cross-platform compatibility
t.Setenv(&quot;HOME&quot;, homeDir)
t.Setenv(&quot;USERPROFILE&quot;, homeDir)</code></pre>
<p><code>config_file_test.go</code>에는 주석까지 달려 있는데, 다른 두 곳이 이 관례를 빠뜨렸다.</p>
<h2 id="삽질-파이프라인이-job-0개로-죽는다">삽질: 파이프라인이 job 0개로 죽는다</h2>
<p>MR을 올리기 전에 CI가 통과하는지 보고 싶었는데 파이프라인이 계속 실패했다. 알림 메일에는 &quot;0 failed jobs&quot;라고만 적혀 있었다. 잡이 하나도 실패하지 않았는데 파이프라인이 실패한 것이다.</p>
<p>REST API에는 사유가 없다. GraphQL로 캐니까 나왔다.</p>
<pre><code>failureReason: The pipeline failed due to the user not being verified.</code></pre><p>GitLab.com 무료 계정은 신원 인증 전까지 공유 러너를 못 쓴다. <code>gitlab.com/-/identity_verification</code>에서 인증하면 풀린다. 과금은 없다.</p>
<p>그리고 하나 더. 브랜치를 푸시해도 파이프라인이 안 돈다. 이 프로젝트의 <code>workflow.rules</code>가 <code>merge_request_event</code>, 태그, 스케줄, 기본 브랜치만 허용하기 때문이다.</p>
<p><strong>업스트림에 MR을 올리기 전에 CI를 확인하려면, 포크 안에서 포크의 main을 타겟으로 MR을 하나 열어보면 된다.</strong> 진짜 <code>merge_request_event</code> 파이프라인이 돌고, 확인한 뒤 닫으면 업스트림에는 아무 흔적도 남지 않는다.</p>
<h2 id="정리">정리</h2>
<p>기여할 거리를 찾을 때 코드 자체보다 <strong>어긋남</strong>이 할게 많았었다.</p>
<ul>
<li>deprecated로 표시해두고 도움말은 안 고친 곳</li>
<li>문서를 생성해두고 소스 예시는 놔둔 곳</li>
<li>한 플랫폼 기준으로 짜놓고 다른 플랫폼에서 안 돌려본 테스트</li>
</ul>
<p>셋 다 리뷰에서 눈에 안 띈다. 서로 다른 파일에 흩어져 있어서다. 대신 <code>grep</code> 대조나 다른 OS에서 한 번 돌려보는 것만으로 나온다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스터디 카페 앱 제작 #5]]></title>
            <link>https://velog.io/@ihopenre-eng/%EC%8A%A4%ED%84%B0%EB%94%94-%EC%B9%B4%ED%8E%98-%EC%95%B1-%EC%A0%9C%EC%9E%91-5</link>
            <guid>https://velog.io/@ihopenre-eng/%EC%8A%A4%ED%84%B0%EB%94%94-%EC%B9%B4%ED%8E%98-%EC%95%B1-%EC%A0%9C%EC%9E%91-5</guid>
            <pubDate>Wed, 15 Jul 2026 07:19:58 GMT</pubDate>
            <description><![CDATA[<p>4편에서는 결제 증빙, 알림, 채팅, PWA, 보안처럼 실제 운영을 위해 다듬었던 부분을 정리해봤습니다. 이번에는 앱 기능 자체보다, 만든 앱을 실제 서비스처럼 공개하기 위해 했던 부가 작업들을 적어보려고 합니다.</p>
<p>처음에는 앱만 잘 만들면 끝이라고 생각했습니다. 예약도 되고, 관리자 페이지도 있고, 모바일에서도 어느 정도 동작하면 바로 쓸 수 있을 줄 알았습니다. 그런데 막상 외부에서 접속할 수 있게 만들려고 하니까 로그인, 도메인, 배포, 검색 색인 같은 작업이 따로 필요했습니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/802624d1-4070-4acc-a651-727baf2c1ce3/image.png" alt=""></p>
<p>먼저 구글 로그인을 붙여봤습니다. 일반 회원가입만 있어도 되긴 했지만, 사용자가 매번 이메일과 비밀번호를 입력하는 것보다 구글 계정으로 바로 로그인하는 게 더 편할 것 같았습니다. 그래서 Google OAuth를 추가해봤습니다.</p>
<p>처음에는 버튼만 붙이면 바로 될 줄 알았습니다. 그런데 실제로 해보니까 생각보다 잘 안 됐습니다. 로컬에서는 되는 것 같은데 배포 환경에서는 안 되거나, 버튼이 제대로 렌더링되지 않거나, 모바일 브라우저에서 버튼이 잘리는 문제가 있었습니다. 특히 Google Identity Services는 등록된 도메인과 redirect 설정이 맞지 않으면 바로 막혀서, 승인된 origin과 callback 흐름을 계속 확인해야 했습니다.</p>
<p>그래서 구글 로그인은 단순히 프론트에 버튼을 붙이는 작업이 아니라, 서버에서 토큰을 검증하고, 배포 도메인을 OAuth 설정에 맞추고, 브라우저별 렌더링 문제까지 같이 봐야 했습니다. 처음에는 로그인 버튼 UI 문제인 줄 알았는데, 실제로는 OAuth 설정, 쿠키, 도메인, 브라우저 정책이 다 연결되어 있었습니다.</p>
<p>카카오 로그인도 추가해봤습니다. 한국 사용자에게는 카카오 로그인이 익숙하기 때문에 넣어두면 접근성이 좋아질 것 같았습니다. 그런데 카카오 로그인도 redirect URI 때문에 많이 막혔습니다. 개발할 때 쓰는 주소와 실제 배포 주소가 다르면 로그인이 실패했고, callback 이후 세션을 앱으로 넘기는 부분도 맞춰야 했습니다.</p>
<p>처음에는 redirect URI를 고정값처럼 넣어봤는데, 배포 환경이 바뀌면 다시 문제가 생겼습니다. 그래서 실제 요청이 들어온 host를 기준으로 redirect URI를 처리하도록 수정했습니다. 이렇게 바꾸니까 로컬과 배포 환경 차이 때문에 생기던 문제를 줄일 수 있었습니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/758a70ae-52c9-4436-9274-5b2da8425a96/image.png" alt=""></p>
<p>도메인 연결도 해봤습니다. 배포 플랫폼에서 제공하는 기본 주소로도 접속은 가능했지만, 실제 서비스처럼 보이려면 도메인이 필요했습니다. 도메인을 연결하면서 DNS 설정, HTTPS 적용, 프론트와 백엔드 주소 분리 같은 부분을 확인했습니다. 이때부터는 “코드가 동작한다”와 “서비스처럼 접속된다”가 다른 문제라는 걸 느꼈습니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/ce796d9c-8db1-4cd4-b969-b4d1c98b51a2/image.png" alt=""></p>
<p>검색 노출도 신경 써봤습니다. 앱을 만들었는데 검색했을 때 아무것도 안 나오면 외부에서 찾기 어렵기 때문에 Google Search Console에 등록해봤습니다. 처음에는 그냥 사이트 주소만 넣으면 되는 줄 알았는데, 실제로는 소유권 확인, sitemap.xml, robots.txt, 색인 요청 같은 작업이 필요했습니다.</p>
<p>소유권 확인을 위해 구글에서 제공하는 인증 파일을 추가하고, 검색 엔진이 어떤 페이지를 읽어야 하는지 알려주기 위해 sitemap.xml도 만들었습니다. 그리고 검색을 막지 않도록 robots.txt도 정리했습니다. 처음에는 이런 파일들이 별로 중요하지 않을 줄 알았는데, 검색 노출을 하려면 기본 설정이 꽤 중요했습니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/e0d3dbf3-3615-4cac-83a1-4ab57eecb8c2/image.png" alt=""></p>
<p>색인 작업을 하면서도 바로 검색 결과에 뜨지는 않았습니다. 등록했다고 즉시 반영되는 게 아니라, 구글이 페이지를 크롤링하고 색인할 때까지 시간이 필요했습니다. 그래서 Search Console에서 색인 요청을 하고, 사이트맵이 정상적으로 읽히는지 확인했습니다. 이 과정에서 앱도 결국 웹 서비스이기 때문에 검색 엔진이 이해할 수 있는 구조가 필요하다는 걸 알게 됐습니다.
배포 후에는 캐시 문제도 많이 겪었습니다. 코드를 수정했는데 실제 사이트에서는 예전 화면이 계속 보이는 경우가 있었습니다. 처음에는 배포가 안 된 줄 알았는데, 알고 보니 브라우저 캐시나 서비스 워커 캐시가 남아 있는 문제였습니다. 그래서 서비스 워커 캐시 버전을 올리고, HTML이나 JS 파일은 너무 오래 캐시되지 않도록 설정을 바꿨습니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/6b3abafe-4082-4a22-a03f-5b0290459cbe/image.png" alt=""></p>
<p>특히 PWA를 적용하면 장점도 있지만, 캐시 관리가 더 중요해졌습니다. 사용자는 앱처럼 빠르게 접근할 수 있지만, 개발자는 업데이트가 제대로 반영되는지 계속 신경 써야 했습니다. 기능을 고쳤는데 사용자가 예전 파일을 보고 있으면 실제로는 버그가 고쳐지지 않은 것처럼 보이기 때문입니다.
이 과정에서 느낀 점은, 앱을 만드는 것과 앱을 공개하는 것은 다른 단계라는 것이었습니다. 예약 기능을 만들고 관리자 페이지를 만드는 건 제품의 핵심 기능을 구현하는 일이었습니다. 하지만 OAuth, 도메인, 검색 색인, 배포 설정, 캐시 처리는 그 제품을 실제 사용자가 접근할 수 있는 서비스로 만드는 일이었습니다.</p>
<p>처음에는 부가 작업이라고 생각했지만, 실제로는 서비스 완성도에 큰 영향을 주는 부분이었습니다. 로그인이 안 되면 사용자는 시작도 못 하고, 도메인이 불안정하면 신뢰가 떨어지고, 검색에 안 잡히면 찾기 어렵고, 캐시가 꼬이면 수정한 기능이 반영되지 않습니다.</p>
<p>이번 프로젝트를 하면서 단순히 기능을 만드는 것뿐만 아니라, 실제 서비스처럼 운영되기 위해 필요한 주변 작업들도 경험해볼 수 있었습니다. OAuth 설정, 도메인 연결, 검색 색인, 배포 환경 차이, 캐시 문제까지 직접 부딪혀보면서 웹앱을 공개하는 과정이 생각보다 넓다는 걸 알게 됐습니다.
다음에는 이 프로젝트를 전체적으로 돌아보면서, 처음 생각했던 것과 달랐던 점, 가장 오래 걸렸던 부분, 그리고 다시 만든다면 다르게 설계하고 싶은 부분들을 정리해보려고 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스터디 카페 앱 제작 #4]]></title>
            <link>https://velog.io/@ihopenre-eng/%EC%8A%A4%ED%84%B0%EB%94%94-%EC%B9%B4%ED%8E%98-%EC%95%B1-%EC%A0%9C%EC%9E%91-4</link>
            <guid>https://velog.io/@ihopenre-eng/%EC%8A%A4%ED%84%B0%EB%94%94-%EC%B9%B4%ED%8E%98-%EC%95%B1-%EC%A0%9C%EC%9E%91-4</guid>
            <pubDate>Wed, 15 Jul 2026 07:11:38 GMT</pubDate>
            <description><![CDATA[<p>3편에서는 관리자 페이지를 만들면서 예약 승인, 좌석 모니터, 회원 관리, 매출 정리 같은 기능을 어떻게 추가했는지 정리해봤습니다. 이번에는 기능을 만든 뒤 실제로 운영할 수 있게 다듬었던 부분들을 적어보려고 합니다.</p>
<p>처음에는 예약 기능과 관리자 페이지가 있으면 어느 정도 완성됐다고 생각했습니다. 사용자가 예약하고, 관리자가 승인하고, 좌석 상태를 확인할 수 있으면 기본 흐름은 돌아가기 때문입니다. 그런데 실제 운영을 생각해보니까 그것만으로는 부족했습니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/5a4d09b3-5693-4720-b09d-9900c968c11b/image.png" alt=""></p>
<p>가장 먼저 신경 쓴 부분은 결제 확인이었습니다. 기존에는 돈 관리도 수기로 하고 있었기 때문에, 온라인 예약을 받으려면 결제 여부를 확인할 수 있어야 했습니다. 처음에는 예약 정보에 결제 방식만 넣어봤습니다. 현장 결제인지, 온라인 결제인지 정도만 구분하면 될 줄 알았습니다.</p>
<p>그런데 온라인 결제는 말로만 “결제했습니다”라고 받으면 관리자가 확인하기 어렵습니다. 그래서 결제 캡처 이미지를 업로드하게 해봤습니다. 사용자가 온라인 결제를 선택하면 결제 증빙을 올리고, 관리자는 관리자 페이지에서 그 이미지를 보고 승인할 수 있게 했습니다.</p>
<p>처음에는 업로드 파일을 서버에 저장하는 방식으로 해봤는데, 배포 환경에서는 로컬 파일 저장이 안정적이지 않을 수 있었습니다. 그래서 나중에는 Cloudinary를 연결해서 업로드 이미지를 외부 저장소에 보관하도록 바꿨습니다. 그리고 아무 URL이나 결제 증빙으로 들어오면 안 되기 때문에, 결제 증빙 URL도 검증하도록 수정했습니다.</p>
<p>알림 기능도 추가해봤습니다. 처음에는 사용자가 예약을 넣고 나면 직접 새로고침해서 상태를 확인해야 했습니다. 그런데 실제 사용자 입장에서는 예약이 승인됐는지, 거절됐는지, 취소됐는지 바로 알 수 있어야 했습니다. 그래서 예약 승인, 거절, 취소, 연장 요청 같은 이벤트가 생기면 알림을 보내는 구조를 만들었습니다.</p>
<p>이 과정에서도 생각보다 고려할 게 많았습니다. 알림 전송이 실패했다고 예약 처리까지 실패하면 안 됐습니다. 예약 승인은 정상적으로 처리하고, 알림은 실패하더라도 전체 기능이 멈추지 않게 해야 했습니다. 그래서 예약 처리 로직과 알림 전송을 분리해서, 알림은 부가적으로 실행되도록 만들었습니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/3b54b907-1c81-437e-a67d-a373e470c267/image.png" alt=""></p>
<p>채팅 기능도 넣어봤습니다. 처음에는 예약 관련 문의가 있으면 따로 연락하면 된다고 생각했는데, 운영을 생각하면 사용자와 관리자가 앱 안에서 바로 문의할 수 있는 게 편했습니다. 그래서 사용자-관리자 채팅을 만들고, 이미지 첨부도 가능하게 했습니다.</p>
<p>처음 채팅 이미지를 처리할 때는 보안 문제가 생길 수 있었습니다. 이미지 URL이나 첨부 데이터를 그대로 화면에 넣으면 XSS 같은 문제가 생길 수 있어서, 클릭 이벤트에 데이터를 직접 넣는 방식을 피하고 안전하게 렌더링하도록 수정했습니다. 단순히 “이미지가 보이면 된다”가 아니라, 사용자가 올리는 데이터는 항상 조심해서 다뤄야 한다는 걸 느꼈습니다.</p>
<p>PWA도 적용해봤습니다. 앱처럼 홈 화면에 추가해서 사용할 수 있으면 직원이나 사용자가 더 편하게 접근할 수 있을 것 같았습니다. 그래서 manifest, 아이콘, 서비스 워커 캐시를 추가했습니다. 처음에는 캐시를 넣으면 성능이 좋아질 줄 알았는데, 실제로는 캐시 때문에 오래된 화면이 남거나 Safari에서 리다이렉션 문제가 생기기도 했습니다.</p>
<p>그래서 서비스 워커 캐시 버전을 계속 올리고, HTML이나 JS처럼 자주 바뀌는 파일은 캐시 정책을 조정했습니다. 특히 관리자 페이지처럼 최신 데이터가 중요한 화면은 캐시가 오히려 문제가 될 수 있어서 더 조심해야 했습니다.</p>
<p>모바일 대응도 오래 걸렸습니다. 실제 사용자는 대부분 모바일로 접속할 가능성이 높았기 때문에, 화면이 PC에서만 잘 보여서는 의미가 없었습니다. 하단 네비게이션, 스크롤, 터치 이벤트, 로그인 버튼, 팝업 위치를 계속 수정했습니다. iOS나 삼성 브라우저에서는 스크롤이 잠기거나 버튼이 잘리는 문제가 있어서, 레이아웃을 여러 번 바꿔봤습니다.</p>
<p>특히 모바일에서는 작은 UI 차이가 사용성을 크게 바꿨습니다. 버튼이 살짝 겹치거나, 팝업이 화면 밖으로 나가거나, 하단 네비게이션이 내용을 가리면 바로 불편해졌습니다. 그래서 모바일에서는 보기 좋은 디자인보다 실제로 눌리고, 스크롤되고, 안 가려지는 구조가 더 중요했습니다.</p>
<p>보안도 중간부터 계속 보강했습니다. 처음에는 기능 구현에 집중했지만, 관리자 페이지와 파일 업로드가 들어가면서 보안 처리가 중요해졌습니다. 관리자 페이지는 프론트에서만 막으면 안 되고, 서버에서도 관리자 권한을 확인해야 했습니다. 그래서 관리자 페이지 접근을 서버에서 다시 검증하도록 바꿨습니다.</p>
<p>또 업로드 파일, 채팅 첨부 이미지, 공지 이미지처럼 사용자가 넣는 데이터는 전부 위험할 수 있다고 보고 검증을 추가했습니다. 요청 횟수 제한, MongoDB 쿼리 sanitizing, CSP, 민감 경로 차단 같은 설정도 넣었습니다. 만들면서 느낀 건, 운영 시스템은 기능이 많아질수록 공격 표면도 같이 넓어진다는 점이었습니다.</p>
<p>결국 4편에서 정리한 작업들은 눈에 확 띄는 핵심 기능은 아닐 수 있습니다. 하지만 실제로 서비스를 운영하려면 꼭 필요한 부분들이었습니다. 결제 증빙이 있어야 관리자가 신뢰할 수 있고, 알림이 있어야 사용자가 상태를 알 수 있고, 채팅이 있어야 문의가 줄어들고, PWA와 모바일 대응이 되어야 실제 환경에서 쓸 수 있고, 보안 처리가 되어야 관리자 기능을 안전하게 열 수 있었습니다.</p>
<p>처음에는 예약 기능만 완성하면 끝이라고 생각했습니다. 그런데 실제로는 그 이후부터가 운영 품질을 만드는 과정이었습니다. 기능이 “된다”에서 끝나는 게 아니라, 사용자가 헷갈리지 않고, 관리자가 실수하지 않고, 모바일에서도 안정적으로 동작하고, 보안적으로도 문제가 없게 계속 다듬는 작업이 더 오래 걸렸습니다.</p>
<p>다음 글에서는 앱 기능 자체보다는 서비스를 실제로 공개하기 위해 했던 부가 작업들을 정리해보려고 합니다. 카카오 로그인과 구글 OAuth를 붙이면서 겪었던 문제, 도메인 연결, Google Search Console 색인, sitemap과 robots 설정, 배포 환경에서 생긴 문제들을 다뤄보겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스터디 카페 앱 제작 #3]]></title>
            <link>https://velog.io/@ihopenre-eng/%EC%8A%A4%ED%84%B0%EB%94%94-%EC%B9%B4%ED%8E%98-%EC%95%B1-%EC%A0%9C%EC%9E%91-3</link>
            <guid>https://velog.io/@ihopenre-eng/%EC%8A%A4%ED%84%B0%EB%94%94-%EC%B9%B4%ED%8E%98-%EC%95%B1-%EC%A0%9C%EC%9E%91-3</guid>
            <pubDate>Wed, 15 Jul 2026 07:05:14 GMT</pubDate>
            <description><![CDATA[<p>2편에서는 3층/4층 좌석권과 M층 시간권을 다르게 처리했던 예약 구조에 대해 정리해봤습니다. 이번에는 관리자 페이지를 만들면서 겪었던 부분을 적어보려고 합니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/e3e6f822-c45a-446c-8d95-176a5d5f717a/image.png" alt=""></p>
<p>처음에는 관리자 페이지를 단순하게 생각했습니다. 사용자가 예약을 신청하면 관리자가 그 예약을 보고 승인하거나 거절하면 된다고 생각했습니다. 그래서 처음에는 예약 목록, 승인 버튼, 거절 버튼 정도만 만들어봤습니다.
그런데 실제 운영을 생각해보니까 예약 목록만으로는 부족했습니다. 직원 입장에서는 “예약이 들어왔는지”보다 “지금 어떤 자리가 사용 중인지”, “누가 언제까지 쓰는지”, “결제가 확인됐는지”, “이 예약이 매출에 들어가도 되는지”를 바로 확인하는 게 더 중요했습니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/6558e6ea-4b76-4478-ad8d-a576f2e96a9d/image.png" alt=""></p>
<p>그래서 먼저 실시간 좌석 모니터를 만들어봤습니다. 좌석마다 비어 있음, 사용 중, 승인 대기 상태를 표시하게 했습니다. 기존에는 직원이 직접 층을 올라가서 자리를 확인해야 했는데, 이제는 관리자 화면에서 바로 볼 수 있게 만들고 싶었습니다.</p>
<p>처음에는 좌석 상태만 표시하면 될 줄 알았습니다. 그런데 막상 써보니까 좌석을 클릭했을 때 그 자리를 누가 쓰고 있는지, 언제까지 이용하는지, 어떤 이용권인지까지 보여줘야 했습니다. 그래서 좌석 클릭 시 예약자 정보와 이용 기간을 확인할 수 있게 수정했습니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/b24dec83-5e5c-4df5-bd61-d4f61f90ad21/image.png" alt=""></p>
<p>회원 관리도 추가해봤습니다. 처음에는 사용자가 직접 가입하고 예약하면 된다고 생각했는데, 실제 운영에서는 관리자가 직접 회원을 등록하거나 정보를 수정해야 하는 경우도 있었습니다. 그래서 관리자 페이지에서 회원 등록, 비밀번호 변경, 전화번호 수정, 회원별 예약 조회 기능을 넣었습니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/34aaf310-3242-40a7-9351-ecbd13ee514a/image.png" alt=""></p>
<p>이용권 관리도 생각보다 중요했습니다. 예약을 잘못 넣었거나 현장에서 이용권을 바꾸는 경우가 있을 수 있어서, 관리자가 1일권, 7일권, 30일권 같은 이용 기간을 수정할 수 있게 했습니다. 처음에는 단순히 날짜만 바꾸면 된다고 생각했는데, 기간이 바뀌면 다른 예약과 겹칠 수도 있어서 다시 중복 검사를 넣어야 했습니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/be5c9242-10db-4f02-b06b-b2270268d82b/image.png" alt=""></p>
<p>매출 관리도 수기 업무를 줄이기 위해 넣었습니다. 기존에는 돈 관리도 따로 수기로 정리하고 있었기 때문에, 예약 데이터를 기준으로 매출을 계산해보려고 했습니다. 그런데 여기서도 문제가 있었습니다. 예약 데이터가 있다고 해서 전부 매출로 보면 안 됐습니다. 취소된 예약, 거절된 예약, 테스트 예약까지 합산되면 실제 매출과 맞지 않았습니다.</p>
<p>그래서 예약 상태별로 매출 반영 여부를 나누고, 테스트 예약은 매출에서 제외할 수 있게 만들었습니다. 처음에는 단순히 전체 예약 금액을 더하는 방식으로 해봤는데, 실제 운영 데이터로 쓰려면 예외 처리가 훨씬 중요하다는 걸 느꼈습니다.</p>
<p>환불 처리도 나중에 추가했습니다. 이용자가 중간에 취소하면 사용한 날짜, 이용권 종류, 무료 음료 사용 여부 같은 걸 고려해야 했습니다. 처음에는 환불을 수기로 계산해도 되지 않을까 생각했는데, 관리자 페이지에서 바로 계산하고 기록할 수 있어야 나중에 헷갈리지 않을 것 같았습니다. 그래서 환불 금액과 사유를 남길 수 있게 정리했습니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/0e278661-4782-4963-8994-9edc5aa64c09/image.png" alt=""></p>
<p>관리자 권한도 분리해봤습니다. 처음에는 관리자 계정 하나로 모든 기능을 쓰게 했는데, 실제로는 직원이 할 수 있는 일과 사장님만 해야 하는 일이 달랐습니다. 예를 들어 예약 확인이나 좌석 관리는 직원도 할 수 있지만, 민감한 초기화나 중요한 운영 기록 삭제는 사장님 권한으로 제한하는 게 맞다고 생각했습니다. 그래서 admin과 owner 권한을 나눴습니다.</p>
<p>만들면서 느낀 점은 관리자 페이지는 단순히 기능을 많이 넣는다고 좋은 게 아니라는 점이었습니다. 운영자가 실수하지 않게 만드는 게 더 중요했습니다. 잘못된 예약을 승인하지 않게 하고, 매출이 중복으로 잡히지 않게 하고, 좌석 상태가 실제 운영과 다르게 보이지 않게 계속 수정해야 했습니다.</p>
<p>처음에는 예약 승인용 화면으로 시작했지만, 만들다 보니 관리자 페이지는 카페 운영의 중심이 됐습니다. 좌석 확인, 예약 승인, 회원 관리, 이용권 수정, 매출 정리, 환불 처리까지 기존에 수기로 하던 업무를 한 화면 안으로 옮기는 작업이었습니다.</p>
<p>다음 글에서는 운영 품질을 높이기 위해 추가했던 기능들을 정리해보려고 합니다. 결제 증빙 업로드, 알림, 채팅, PWA, 모바일 브라우저 대응, 보안 패치 같은 부분을 다뤄보겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스터디 카페 앱 제작 #2]]></title>
            <link>https://velog.io/@ihopenre-eng/%EC%8A%A4%ED%84%B0%EB%94%94-%EC%B9%B4%ED%8E%98-%EC%95%B1-%EC%A0%9C%EC%9E%91-2</link>
            <guid>https://velog.io/@ihopenre-eng/%EC%8A%A4%ED%84%B0%EB%94%94-%EC%B9%B4%ED%8E%98-%EC%95%B1-%EC%A0%9C%EC%9E%91-2</guid>
            <pubDate>Wed, 15 Jul 2026 06:55:16 GMT</pubDate>
            <description><![CDATA[<p>1편에서는 왜 카페 운영 시스템을 만들게 되었는지 정리해봤습니다. 이번에는 이 앱에서 가장 중요했던 예약 구조에 대해 적어보려고 합니다.</p>
<p>처음에는 예약 기능을 단순하게 생각했습니다. 사용자가 자리를 고르고, 날짜를 선택하고, 예약을 신청하면 끝이라고 생각했습니다. 그래서 처음에는 모든 예약을 거의 같은 방식으로 처리해보려고 했습니다. 좌석 번호, 날짜, 이용 기간 정도만 있으면 될 것 같았습니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/0b345101-2e8c-4165-a0e6-31cab9eccc3f/image.png" alt=""></p>
<p>그런데 실제로 만들어보니까 3층/4층 예약과 M층 예약은 완전히 다른 방식으로 봐야 했습니다.</p>
<p>3층과 4층은 좌석권 중심이었습니다. 예를 들어 1일권, 7일권, 30일권처럼 특정 좌석을 며칠 동안 사용하는 구조였습니다. 그래서 중요한 값은 “어떤 좌석을”, “언제부터”, “며칠 동안” 사용하는지였습니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/e3e6b8c9-0eb6-4452-b445-52377694b8af/image.png" alt=""></p>
<p>반면 M층은 시간권 중심이었습니다. 좌석 하나를 며칠 동안 잡아두는 방식이 아니라, 특정 날짜의 특정 시간대에 공간을 예약하는 방식이었습니다. 그래서 “몇 월 며칠”, “몇 시부터”, “몇 시까지”가 중요했습니다. 이걸 3층/4층 좌석권과 똑같이 처리하려고 하니까 바로 문제가 생겼습니다.</p>
<p>처음에는 M층도 그냥 날짜와 이용 시간만 저장하면 되겠다고 생각했습니다. 그런데 그렇게 해보니까 같은 시간대에 예약이 겹치는 문제가 생길 수 있었습니다. 예를 들어 누군가 오후 2시부터 4시까지 예약했는데, 다른 사용자가 오후 3시부터 5시까지 예약하면 둘 다 같은 시간대를 쓰게 됩니다. 단순히 날짜만 비교하면 이런 충돌을 막을 수 없었습니다.</p>
<p><img src="https://velog.velcdn.com/images/ihopenre-eng/post/b0da1f61-5f8d-476e-8c4d-85d869bb9cd2/image.png" alt=""></p>
<p>그래서 M층 예약은 시작 시간과 종료 시간을 따로 저장하고, 새 예약이 들어올 때 기존 예약들과 시간이 겹치는지 검사하도록 바꿨습니다. 기준은 단순했습니다. 새 예약의 시작 시간이 기존 예약의 종료 시간보다 빠르고, 새 예약의 종료 시간이 기존 예약의 시작 시간보다 늦으면 겹치는 예약으로 보는 방식입니다. 이렇게 바꾸니까 같은 날짜 안에서 시간대가 겹치는 예약을 막을 수 있었습니다.</p>
<p>또 하나 막혔던 부분은 이용 시간 단위였습니다. 처음에는 1시간 단위로만 예약하면 충분할 줄 알았습니다. 그런데 실제 운영에서는 1시간 30분 같은 예약도 필요할 수 있었습니다. 그래서 M층은 30분 단위 예약을 지원하도록 바꿨습니다. 시작 시간도 30분 단위로 선택할 수 있게 하고, 이용 시간도 1시간, 1시간 30분처럼 처리할 수 있게 수정했습니다.</p>
<p>이 과정에서 생각보다 자잘한 버그도 많이 나왔습니다. 예를 들어 M층은 일반 좌석처럼 좌석 번호가 명확하지 않아서 내부적으로 0 같은 값을 쓰는 경우가 있었는데, 처음 유효성 검사에서는 0을 잘못된 값처럼 처리하는 문제가 있었습니다. JavaScript에서 0은 falsy 값이라 단순 조건문으로 검사하면 없는 값처럼 판단될 수 있었습니다. 그래서 좌석 번호가 있는지 없는지를 대충 검사하지 않고, 숫자인지와 허용 범위 안에 있는지를 명확하게 검사하도록 바꿨습니다.</p>
<p>예약 상태도 처음보다 더 세분화했습니다. 처음에는 예약이 있냐 없냐 정도만 생각했는데, 실제 운영에서는 그걸로 부족했습니다. 사용자가 예약을 신청했지만 아직 관리자가 확인하지 않은 상태, 관리자가 승인한 상태, 거절된 상태, 사용자가 취소한 상태, 이용이 끝난 상태가 모두 달랐습니다. 그래서 예약 상태를 대기, 승인, 거절, 취소, 완료처럼 나눴습니다.</p>
<p>이렇게 상태를 나누니까 관리자 페이지도 훨씬 명확해졌습니다. 관리자는 어떤 예약을 승인해야 하는지, 어떤 좌석이 실제 사용 중인지, 어떤 예약은 매출에 반영해도 되는지를 구분할 수 있었습니다. 특히 매출 계산에서는 단순히 예약 데이터가 있다고 전부 더하면 안 됐습니다. 거절된 예약이나 취소된 예약, 테스트 예약까지 매출에 들어가면 실제 운영 데이터가 틀어지기 때문입니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/1b8a2048-6259-459d-96ef-845520f18b40/image.png" alt=""></p>
<p>반복 예약도 쉽지 않았습니다. M층은 한 번만 예약하는 경우도 있지만, 매주 같은 요일에 반복해서 예약하는 경우도 필요했습니다. 처음에는 반복 예약을 단순히 여러 개의 예약으로 생성하면 된다고 생각했습니다. 그런데 실제로는 각 날짜마다 기존 예약과 시간이 겹치는지 모두 확인해야 했습니다. 하나라도 겹치면 전체 예약을 막거나, 사용자가 다시 선택할 수 있게 해야 했습니다. 그래서 반복 예약도 결국 “여러 개를 한 번에 만드는 기능”이 아니라, 각각의 날짜와 시간대에 대해 충돌 검사를 하는 구조로 봐야 했습니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/d5e10e0f-dfac-4103-b17a-c641e212afe2/image.png" alt=""></p>
<p>결국 예약 기능을 만들면서 가장 크게 느낀 점은, 같은 “예약”이라는 단어를 쓰더라도 운영 방식에 따라 데이터 구조가 달라져야 한다는 것이었습니다. 3층/4층은 좌석과 기간이 핵심이고, M층은 날짜와 시간이 핵심이었습니다. 이 차이를 제대로 분리하지 않으면 화면은 그럴듯하게 보여도 실제 운영에서는 바로 문제가 생겼습니다.</p>
<p>처음에는 단순히 예약 폼을 만드는 작업이라고 생각했지만, 실제로는 카페 운영 정책을 데이터 구조로 옮기는 작업이었습니다. 어떤 예약을 허용할지, 어떤 예약을 막을지, 어떤 상태를 관리자에게 보여줄지 하나씩 정리하면서 시스템이 점점 실제 운영에 맞게 바뀌었습니다.</p>
<p>다음 글에서는 관리자 페이지를 어떻게 만들었는지 정리해보려고 합니다. 예약 목록만 보여주는 화면에서 시작해서, 좌석 모니터, 회원 관리, 매출 정리, 환불 처리까지 어떻게 확장됐는지를 다뤄보겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스터디 카페 앱 제작 #1]]></title>
            <link>https://velog.io/@ihopenre-eng/%EC%8A%A4%ED%84%B0%EB%94%94-%EC%B9%B4%ED%8E%98-%EC%95%B1-%EC%A0%9C%EC%9E%91-1</link>
            <guid>https://velog.io/@ihopenre-eng/%EC%8A%A4%ED%84%B0%EB%94%94-%EC%B9%B4%ED%8E%98-%EC%95%B1-%EC%A0%9C%EC%9E%91-1</guid>
            <pubDate>Wed, 15 Jul 2026 06:47:07 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ihopenre-eng/post/6bac50d9-351b-4a1b-8552-27939f686c1a/image.png" alt=""></p>
<p>이 개발을 시작하게 된 계기는 학원 선생님께서 만들어보면 어떻겠냐고 말씀해주신 것이었습니다. 처음에는 그냥 카페 좌석 예약 페이지 정도를 만들어보면 되겠다고 생각했습니다. 사용자가 자리를 고르고 예약하면 관리자가 확인하는 정도의 간단한 시스템을 떠올렸습니다.</p>
<p><img src="https://velog.velcdn.com/images/ihopenre-eng/post/f80df32b-4ec9-43e0-b264-133d8816485d/image.png" alt=""></p>
<p>그런데 실제 운영 방식을 보니까 생각보다 불편한 부분이 많았습니다. 직원분들이 매번 직접 층을 올라가서 어떤 자리가 비어 있는지 하나씩 확인해야 했고, 이용권도 1일권, 7일권, 30일권처럼 종류별로 따로 수기로 기록하고 있었습니다. 돈 관리도 따로 수기로 정리하고 있었고, 예약 상태나 결제 여부를 한눈에 볼 수 있는 화면도 없었습니다.</p>
<p>그래서 단순한 예약 페이지가 아니라, 카페 운영에서 수기로 하던 일을 온라인으로 정리하는 시스템을 만들어보기로 했습니다.
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/200c87e3-475f-4f7f-9a44-2f7f8724fceb/image.png" alt=""></p>
<p>먼저 좌석을 온라인에서 확인할 수 있게 만들어봤습니다. 처음에는 “빈 자리인지, 사용 중인지 정도만 표시하면 되겠지”라고 생각했는데, 막상 해보니까 그것만으로는 부족했습니다. 예약 신청만 들어온 상태인지, 관리자가 승인한 예약인지, 취소된 예약인지, 이용 기간이 끝난 예약인지가 다 구분되어야 했습니다. 그래서 예약 상태를 대기, 승인, 거절, 취소, 완료처럼 나누고, 관리자 화면에서 바로 확인할 수 있게 정리했습니다.</p>
<p>이용권 관리도 생각보다 까다로웠습니다. 3층과 4층은 좌석을 며칠 동안 쓰는 방식이라 1일권, 7일권, 30일권처럼 기간 중심으로 처리하면 됐습니다. 그런데 M층은 시간 단위로 예약하는 공간이라 날짜뿐만 아니라 시작 시간과 이용 시간까지 계산해야 했습니다. 처음에는 단순하게 처리해봤는데, 같은 시간대에 중복 예약이 생길 수 있어서 시간 충돌을 따로 검사하도록 수정했습니다.</p>
<p><img src="https://velog.velcdn.com/images/ihopenre-eng/post/0f37ff3e-0297-4686-9f0e-3c4f18ca548f/image.png" alt=""></p>
<p>관리자 페이지도 계속 바꿔봤습니다. 처음에는 예약 목록만 보여주면 될 줄 알았는데, 실제로는 관리자가 보고 싶은 정보가 훨씬 많았습니다. 지금 어떤 좌석이 사용 중인지, 누가 언제까지 이용하는지, 결제가 확인됐는지, 매출에 반영해도 되는 예약인지가 중요했습니다. 그래서 좌석 모니터, 예약 승인, 회원 관리, 매출 정리, 환불 처리 같은 기능을 하나씩 추가했습니다.</p>
<p><img src="https://velog.velcdn.com/images/ihopenre-eng/post/fac259bb-25a9-41c4-8ba9-8863fd90fc18/image.png" alt=""></p>
<p>만들면서 가장 크게 느낀 점은, 실제 운영 시스템은 기능 하나하나보다 예외 처리가 더 중요하다는 것이었습니다. 예약이 겹치면 안 되고, 결제 증빙이 있어야 하고, 관리자가 실수로 잘못 승인하지 않게 해야 하고, 모바일에서도 제대로 동작해야 했습니다. 처음에는 간단한 예약 앱으로 시작했지만, 결국 카페에서 수기로 하던 운영 흐름을 온라인으로 옮기는 프로젝트가 되었습니다.</p>
<p>다음 글에서는 이 시스템에서 가장 중요했던 예약 구조를 정리해보려고 합니다. 특히 3층/4층 좌석권과 M층 시간권을 왜 다르게 설계했는지, 그리고 중복 예약 문제를 어떻게 해결했는지를 다뤄보겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[피카츄 배구 메모리 값 수정으로 간단한 해킹]]></title>
            <link>https://velog.io/@ihopenre-eng/%ED%94%BC%EC%B9%B4%EC%B8%84-%EB%B0%B0%EA%B5%AC-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B0%92-%EC%88%98%EC%A0%95%EC%9C%BC%EB%A1%9C-%EA%B0%84%EB%8B%A8%ED%95%9C-%ED%95%B4%ED%82%B9</link>
            <guid>https://velog.io/@ihopenre-eng/%ED%94%BC%EC%B9%B4%EC%B8%84-%EB%B0%B0%EA%B5%AC-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B0%92-%EC%88%98%EC%A0%95%EC%9C%BC%EB%A1%9C-%EA%B0%84%EB%8B%A8%ED%95%9C-%ED%95%B4%ED%82%B9</guid>
            <pubDate>Wed, 15 Jul 2026 06:33:44 GMT</pubDate>
            <description><![CDATA[<h2 id="실습-환경">실습 환경</h2>
<p>Windows
피카츄 배구
Cheat Engine</p>
<p>메모리 값 찾기</p>
<p>먼저 게임을 실행한 후 Cheat Engine으로 프로세스에 연결합니다.</p>
<p>게임에서 현재 점수를 확인한 뒤 해당 숫자를 검색합니다.</p>
<p>예를 들어 점수가 3점이라면 3을 검색하고, 이후 점수가 변경될 때마다 다시 검색(Next Scan)을 반복하면 후보 주소가 점점 줄어듭니다.</p>
<p>최종적으로 실제 점수가 저장되는 메모리 주소를 찾을 수 있습니다.</p>
<h2 id="메모리-값-수정">메모리 값 수정</h2>
<p>점수가 저장된 주소를 찾은 후 값을 원하는 숫자로 변경했습니다.</p>
<p>예를 들어</p>
<p>3 → 99
5 → 100</p>
<p>처럼 수정하면 게임 내 점수가 즉시 변경되는 것을 확인할 수 있습니다.</p>
<p>이는 게임이 화면에 표시하는 값이 메모리에 저장된 데이터를 그대로 사용하기 때문입니다.</p>
<p>실습 결과</p>
<details>
<summary>동영상 보기 (클릭)</summary>
<iframe width="100%" height="400" src="https://www.youtube.com/embed/IEz3-VmQteg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</details>

<p>영상처럼 상대 피카츄가 득점을 해도 메모리 값이 7로 고정되어있기 때문에 7에서 늘어나지 않는 모습을 볼수있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Hak5 usb 를 이용하여 자동 인증서 설치 및 서버 접속을 통하여 실시간 원격 트래픽 분석하기(mitmproxy/Rubber Ducky)]]></title>
            <link>https://velog.io/@ihopenre-eng/Hak5-usb-%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EC%9E%90%EB%8F%99-%EC%9D%B8%EC%A6%9D%EC%84%9C-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%9C%EB%B2%84-%EC%A0%91%EC%86%8D%EC%9D%84-%ED%86%B5%ED%95%98%EC%97%AC-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%9B%90%EA%B2%A9-%ED%8A%B8%EB%9E%98%ED%94%BD-%EB%B6%84%EC%84%9D%ED%95%98%EA%B8%B0mitmproxyRubber-Ducky</link>
            <guid>https://velog.io/@ihopenre-eng/Hak5-usb-%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EC%9E%90%EB%8F%99-%EC%9D%B8%EC%A6%9D%EC%84%9C-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%9C%EB%B2%84-%EC%A0%91%EC%86%8D%EC%9D%84-%ED%86%B5%ED%95%98%EC%97%AC-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%9B%90%EA%B2%A9-%ED%8A%B8%EB%9E%98%ED%94%BD-%EB%B6%84%EC%84%9D%ED%95%98%EA%B8%B0mitmproxyRubber-Ducky</guid>
            <pubDate>Wed, 15 Jul 2026 05:36:31 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ihopenre-eng/post/3d0cecf9-a779-4a37-bb8f-e6ff52125e51/image.png" alt=""></p>
<p>일단 이 bad usb 라고 불리는 Hak5 Rubber Ducky라는 기기에 대해서 간단하게 설명해보자면 영화에서 많이 나온 usb를 꼽자마자 자동으로 막 해킹이 되는 장면이 많은데 실제로 그 공격을 수행할수있도록 해주는  기기 입니다. </p>
<p>이 usb에는 payload를 미리 저장하여 자동으로 실행시킬수 있습니다.</p>
<p>이제 트래픽 분석에 대해서 알아보겠습니다.</p>
<p>먼저 공격자 컴퓨터에서 서버를 아래처럼 열고 (8000포트로 수행했습니다)
<img src="https://velog.velcdn.com/images/ihopenre-eng/post/c05e011e-036c-44cf-9ddb-1594961f5964/image.png" alt="">
이제  mitmproxy --listen-port 8000 --showhost 라는 명령어를 통해서 8000포트를 사용하여 통신하는 모든 패킷들을 볼수있게 세팅 해줍니다.<img src="https://velog.velcdn.com/images/ihopenre-eng/post/6221a879-8c9a-4901-807c-9a6a9127dab5/image.png" alt="">
또한 공격을 하기 전에 미리 해야할것이 있는데 바로 인증서를 서버에 업로드 하는 것 입니다. 저는 mitmproxy 트래픽을 가로채기 위한 프록시 툴을 사용하여 공격을 수행하였습니다.</p>
<p>mitmproxy를 사용하여 인증서를 생성하고 상대 컴퓨터에서는 usb가</p>
<p>cd $env:USERPROFILE.mitmproxy
python -m http.server 8000</p>
<p>라는 코드를 실행시켜</p>
<p>아까 생성한 8000포트 서버를 통하여 인증서를 불러오게 시킵니다.</p>
<p>그러면 상대방은 아까 생성한 프록시 서버에 접속하게 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/ihopenre-eng/post/103e4dfe-43cf-4b37-ab12-8cb013fd8a3a/image.png" alt="">
이렇게 상대방이 어떤 패킷을 주고 받는지를 볼수있게 됩니다.</p>
<p>(세션 쿠키,아이디/비밀번호,다운로드중인 파일 내용,현재 입력중인 것들을 실시간으로 확인 가능)</p>
<p>하지만 이 공격은 상대방과 동일한 네트워크에 접속한 상태라는 가정하에 수행가능 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Subway Surfers 무한 점프 만들기 (Cheat Engine/LD player)]]></title>
            <link>https://velog.io/@ihopenre-eng/Subway-Surfers-%EB%AC%B4%ED%95%9C-%EC%A0%90%ED%94%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-Cheat-EngineLD-player</link>
            <guid>https://velog.io/@ihopenre-eng/Subway-Surfers-%EB%AC%B4%ED%95%9C-%EC%A0%90%ED%94%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-Cheat-EngineLD-player</guid>
            <pubDate>Wed, 15 Jul 2026 03:28:05 GMT</pubDate>
            <description><![CDATA[<p>오늘은 Cheat Engine 을 사용하여 간단한 메모리 조작을 통해서 Subway Surfers 라는 게임을 조작해 보았습니다.</p>
<p> <img src="https://velog.velcdn.com/images/ihopenre-eng/post/09f17bc2-756b-4613-8c2d-cba69473b3b1/image.png" alt=""></p>
<p>일단 Unknown initial value 로 scan을 시작합니다. 왜냐하면 게임 내 어떤 변수(예: 체력, 스태미나, 스코어 등)의 정확한 값을 모르기 때문에 사용합니다  예를 들어 체력바가 숫자가 아닌 그래픽으로 표시된다면 값이 몇인지 알 수 없기 때문에 &quot;Unknown initial value&quot;로 시작합니다. 그리고 이 게임은 점프할 때마다 내부 값이 변하기 때문에 점프 전후로 스캔을 반복하면서 그 차이를 통해 관련 값들을 찾아 내야합니다.</p>
<p><img src="https://velog.velcdn.com/images/ihopenre-eng/post/99e45e48-c7a1-4dfc-8ba5-01c9b5e319b8/image.png" alt=""></p>
<p>위에 사진은 게임내에서 캐릭터가 점프할때와 착지 했을때를 changed value로 scan 하여서</p>
<p>점프할수있을때가 1 할수없을때값 0을 측정하여 고정한 것 입니다.</p>
<details>
  <summary>Subway Surfers 무한 점프 영상</summary>
  <br />
  <iframe width="100%" height="500" src="https://www.youtube.com/embed/QzdpxnB_ao0" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</details>



<p>이런식으로 값들을 고정하여 계속해서 점프할수있는 상태를 유지시켜 무한 점프가 가능하도록 메모리를 조작하였습니다.</p>
]]></description>
        </item>
    </channel>
</rss>