<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>2-pi-r.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sun, 10 Aug 2025 15:46:46 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>2-pi-r.log</title>
            <url>https://velog.velcdn.com/images/2-pi-r/profile/3e1780ab-9717-46b5-94ae-7bce9a10912e/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 2-pi-r.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/2-pi-r" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Django] 마이그레이션 충돌 해결]]></title>
            <link>https://velog.io/@2-pi-r/Django-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EC%B6%A9%EB%8F%8C-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@2-pi-r/Django-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EC%B6%A9%EB%8F%8C-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Sun, 10 Aug 2025 15:46:46 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<ul>
<li><p>MVP 개발하는 중이라 DB 구조 변경이 빈번하게 일어남.
백엔드 개발자가 2명이라 동시에 수정하는 경우도 많음.
⇒ 브랜치 머지할 때마다 → 장고 마이그레이션 파일 충돌 문제 발생.
⇒ 배포할 때마다 그러니까 → 서버가 계속 다운됨.</p>
</li>
<li><p>개발보다 마이그레이션 충돌 해결하는 데 시간을 더 많이 씀.</p>
</li>
<li><p>필요하다면 기존 마이그레이션 파일을 삭제하는 방식으로 해결하고 있었는데, 이러니까 기존 테이블을 싹 날리고 다시 생성해야 하는 상황 발생. (더미데이터 쌓았던 거 날리고.)</p>
</li>
</ul>
<h2 id="여태-시도했으나-실패한-방법들">여태 시도했으나 실패한 방법들</h2>
<h4 id="시도1-로컬에서-해결-후-서버-배포">[시도1] 로컬에서 해결 후 서버 배포</h4>
<ul>
<li>작업한 브랜치를 develop 브랜치에 PR 날려서 머지하기 전에 → develop 브랜치를 역으로 현재 브랜치에 pull해서, 로컬에서 마이그레이션 파일을 충돌한 후에 → develop 브랜치로 배포하자.</li>
<li>이유 : 서버가 다운되는 걸 막기 위해. (참고: develop 브랜치에 커밋되면 서버에 자동 배포되게 해놨다.)</li>
<li>문제 : 로컬에선 해결됐어도, 로컬 DB와 서버 DB의 스키마가 다르기 때문에 → 서버에서는 여전히 문제 발생.</li>
</ul>
<h4 id="시도2-로컬에서-개발-시-서버-db-사용">[시도2] 로컬에서 개발 시 서버 DB 사용</h4>
<ul>
<li>로컬에서 개발할 때에도 로컬 DB 대신 서버 DB를 사용하자.</li>
<li>이유 : 방법1로는 충돌을 2번 해결해야 하니까, 로컬에서 해결하는 1번으로 줄여보자.</li>
<li>문제 : AWS RDS DB를 로컬에 연결하면 → 조회하는 데 너무 느렸다.</li>
</ul>
<h3 id="시도3-마이그레이션-파일을-gitignore에-추가">[시도3] 마이그레이션 파일을 gitignore에 추가</h3>
<blockquote>
<p>결론부터 말하자면 이러면 안 되고, 문제가 해결되지도 않았다. 꼭 커밋하자.</p>
</blockquote>
<ul>
<li>초기 개발 단계에선 DB 수정이 잦으므로 마이그레이션 파일 자체를 깃허브에 커밋하지 말고, 추후 DB 구조가 확정되면 그때부터 커밋하자. (gitignore에서 마이그레이션 폴더 삭제)</li>
<li>이유: 로컬, 서버에 각자 마이그레이션 파일 내역을 관리해도 models.py만 공유되면 최종적으로 DB 스키마는 같지 않나.<ul>
<li>나중에 안 건데, 사실 DB가 다를 수 있다. 자세한 건 <a href="https://djangowaves.com/tips-tricks/gitignore-for-a-django-project/#migrations">[여기]</a>를 참고.</li>
</ul>
</li>
<li>문제:<ul>
<li>gitignore에 마이그레이션 파일을 추가하면서 깃허브에서 마이그레이션 파일이 삭제됐다. 그래서 develop 브랜치에 작업을 커밋해서 <strong>서버에 새로 배포하면, 그 전에 서버에서 생성, 적용했던 마이그레이션 파일이 싹 없어진다. develop 브랜치에 마이그레이션 파일이 init.py 빼고 아무것도 없으니까</strong>, 배포하면 서버에도 그렇게 되는 것이다. <strong>(남아있을 줄 알았다...)</strong></li>
<li>그래서 서버에서 makemigration 하면 매번 모든 내용을 0001 init으로 생성한다. 현재 DB가 어떻든간에. 그래서 migrate를 할 수가 없다.<ul>
<li>내가 했을 땐 0001 init이 이미 적용된 상태라서 그런 건지, 명령어 실행에선 오류가 안 나지만 실제로 DB 스키마가 변경되지는 않았다.</li>
<li>내 생각엔, 이미 적용된 operator와 새로 수정해서 적용되어야 할 operator가 모두 0001에 있기 때문에, 어쨌든 문제가 생길 것이다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="해결-방법">해결 방법</h2>
<h3 id="시도4-이게-정석인-것-같다">[시도4] 이게 정석인 것 같다...</h3>
<blockquote>
<h4 id="django-마이그레이션-내역-관리의-📌핵심📌">DJango 마이그레이션 내역 관리의 📌핵심📌</h4>
<ul>
<li>마이그레이션 파일을 모두 커밋하고,<ul>
<li><em>(<a href="https://docs.djangoproject.com/en/3.2/topics/migrations/">공식 문서</a>에서도 커밋하라고 한다. <a href="https://velog.io/@hanqyu/django-migration-%ED%8C%8C%EC%9D%BC%EC%9D%80-%EC%BB%A4%EB%B0%8B%EB%90%98%EC%96%B4%EC%95%BC-%ED%95%A0%EA%B9%8C">추가 참고자료</a>)</em></li>
</ul>
</li>
<li>팀에 공유된 (깃허브에 업로드된) 파일은 삭제/수정 금지</li>
<li>팀원 모두의 로컬, 서버에서 내역을 동일하게 유지하려고 노력하기<ul>
<li><em>(우리 플젝에선 서버 = 깃허브 develop브랜치)</em></li>
</ul>
</li>
</ul>
</blockquote>
<ul>
<li><p>한 번에 한 사람만 수정하고, 다른 사람들한테는 배포 전까지 모델 건드리지 말라고 카톡으로 알려주기</p>
<ul>
<li>이때 앱이 다르면 동시에 수정해도 괜찮다. 마이그레이션 파일이 앱마다 별도로 생성되니까 충돌이 발생하지 않는다.</li>
</ul>
</li>
<li><p>꼭 모델 수정한 사람만 로컬 작업 브랜치에서 mikemigration하기</p>
<ul>
<li>그 외 사람이나 서버에서는 makemigration 금지하고 migrate만 하기</li>
</ul>
</li>
<li><p>충돌이 나더라도 크게 꼬이지 않도록, 수정할 때마다 자주자주 mikemigration하기</p>
<ul>
<li>충돌이 나더라도 안전한 방법이 있으면 장고가 해결책 제안해준다. (merge하라든가.)</li>
<li>근데 많이 꼬이면 이런 거 없이 직접 수정해서 해결해야 된다고.</li>
<li>이때 다른 사람들과 함께 사용하는(깃허브에 올라간) 마이그레이션은 수정하거나 제거하지 않는 것이 원칙.</li>
</ul>
</li>
</ul>
<p>(+) 참고로 0001 같은 번호는 개발자 참조용이라 장고한테는 중요하지 않고, dependency만 중요하다고 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Github Action] 기존 Docker 컨테이너 삭제 안 되는 문제 해결]]></title>
            <link>https://velog.io/@2-pi-r/Github-Action-Docker-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%B9%8C%EB%93%9C-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@2-pi-r/Github-Action-Docker-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%B9%8C%EB%93%9C-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Sun, 10 Aug 2025 13:34:50 GMT</pubDate>
            <description><![CDATA[<h2 id="팀-개발-환경">팀 개발 환경</h2>
<ul>
<li>Github Action<ul>
<li>CI/CD 구축 (Github의 develop 브랜치에 커밋이 되면 → AWS EC2에 자동 배포되도록)</li>
</ul>
</li>
</ul>
<blockquote>
<p><a href="https://velog.io/@2-pi-r/%EC%84%9C%EB%B2%84-%EB%B0%B0%ED%8F%AC-%EC%8B%9C-%EC%9E%90%EC%A3%BC-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EB%AC%B8%EC%A0%9C%EB%93%A4">(자세한 건 이전 게시물 참고)</a></p>
</blockquote>
<h2 id="문제">문제</h2>
<p>Github Action에서 build에 실패했다. (전에는 잘 됐고, 이번에만 갑자기 실패)</p>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/97325861-9ae2-4a4a-955f-2d7c3df108d6/image.png" alt=""></p>
<h2 id="원인-해결-방법">원인, 해결 방법</h2>
<p><code>deploy.yml</code>(workflow file)를 확인하니,
지금은 develop 브랜치에 커밋되면 <code>deploy.sh</code>에서
docker-compose로 컨테이너를 빌드해서 실행하고 있다.
이때 같은 이름의 컨테이너가 있으면 안 돼서, 새 컨테이너 실행 전에 기존 컨테이너를 삭제한다.</p>
<p>문제는 <code>deploy.sh</code>에서 compose로 생성한 컨테이너만 삭제하고 있었다는 거다. 팀원이 어제 배포하고 ec2에서 문제 해결하면서 cli에서 docker run으로 컨테이너를 직접 생성했는지, 그 컨테이너는 삭제가 안 됐다.</p>
<p>그래서 <code>deploy.sh</code>에 한 줄 추가해서 run으로 직접 실행한 컨테이너도 삭제할 수 있도록 했다.</p>
<h2 id="자세한-해결-과정">자세한 해결 과정</h2>
<h3 id="1-로그-확인하기">1. 로그 확인하기</h3>
<blockquote>
<p><a href="https://velog.io/@2-pi-r/%EC%84%9C%EB%B2%84-%EB%B0%B0%ED%8F%AC-%EC%8B%9C-%EC%9E%90%EC%A3%BC-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EB%AC%B8%EC%A0%9C%EB%93%A4">(방법: 이전 게시물 참고)</a></p>
</blockquote>
<p>로그를 확인하면 어느 단계에서 문제가 생겼는지 알 수 있다.</p>
<p>아래 ERROR: 부분을 보면 docker 컨테이너 생성 중에 같은 이름의 컨테이너가 이미 있어서 충돌이 발생하고 있다.</p>
<p>이전 게시물에서 설명했듯, 위쪽 네모가 workflow file에서 name이다. 그걸 찾아보자.</p>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/fceabbcd-18c0-44d5-8d16-c66c2b2a4df4/image.png" alt=""></p>
<h3 id="2-workflow-file에서-1의-name-찾기">2. workflow file에서 1의 name 찾기</h3>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/bfe356f1-465b-41fb-a958-09f432df2e04/image.png" alt=""></p>
<ul>
<li><p>트리거 지정</p>
<ul>
<li>develop 브랜치에 push했을 때</li>
<li>release 브랜치에 PR 날렸을 때</li>
<li>(main 브랜치는 주석 처리 해둠.)<pre><code>on:
push:
  branches:
    # - main
    - develop
pull_request:
  branches:
    # - main
    - release</code></pre></li>
</ul>
</li>
<li><p>실제로 수행할 작업</p>
<pre><code>jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:</code></pre><ul>
<li>steps
```
(생략)<h1 id="서버에-접속한-뒤-deploysh배포-스크립트를-실행">서버에 접속한 뒤 deploy.sh(배포 스크립트)를 실행</h1>
</li>
<li>name: 📌executing remote ssh commands using password📌<pre><code>(생략)
  script: |
    sh /(생략)/config/scripts/deploy.sh</code></pre><pre><code></code></pre></li>
</ul>
</li>
</ul>
<p>1에서 본 name을 찾았다!
(<code>📌executing remote ssh commands using password📌</code>)
물론 실제 코드에 압정 이모티콘은 없다. 설명하려고 표시했다.</p>
<p>여기서 아래 커맨드 실행를 실행하나보다.
<code>sh /(생략)/config/scripts/deploy.sh</code> 
deploy.sh 파일을 까보자.</p>
<h3 id="3-deploysh-확인하기">3. deploy.sh 확인하기</h3>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/218597c9-ce75-4192-83dd-d8b6731a5088/image.png" alt=""></p>
<pre><code># Docker 설치 여부 확인, 없으면 설치
if ! type docker &gt; /dev/null
then
  echo &quot;docker does not exist&quot;
  echo &quot;Start installing docker&quot;
  (생략)

# Docker Compose 설치 여부 확인, 없으면 설치
if ! type docker-compose &gt; /dev/null
then
  echo &quot;docker-compose does not exist&quot;
  echo &quot;Start installing docker-compose&quot;
  (생략)


# 기존 orphan 컨테이너 정리 
echo &quot;stop and remove old containers (with orphans)&quot;
sudo docker-compose -f /(생략)/docker-compose.prod.yml down --remove-orphans

# Docker Compose로 서버 빌드 및 실행
echo &quot;start docker-compose up: ubuntu&quot;
sudo docker-compose -f /(생략)/docker-compose.prod.yml up --build -d</code></pre><p>기존 컨테이너를 정리하는 코드에 문제가 있는 것 같다.</p>
<p>보니까 파일 내용에 echo가 있고, (print문 같은 것)
그게 로그에 출력되는 것 같다.</p>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/ed2ae09d-eae6-4329-a646-88782d150a2e/image.png" alt=""></p>
<p>Docker, Docker Compose 설치는 이미 되어있어서 실행되지 않았고,
아래 부분은 잘 실행되고 있다.</p>
<ul>
<li>기존 컨테이너 정리</li>
<li>Docker Compose로 컨테이너 빌드 및 실행</li>
</ul>
<blockquote>
<p>결론 : 기존 컨테이너를 정리하는 명령어가 실행되지만, 제대로 정리되지 않았다.
<code>sudo docker-compose -f /(생략)/docker-compose.prod.yml down --remove-orphans</code>
같은 명령어를 EC2 cli에서 직접 실행해도 여전히 기존 컨테이너가 삭제되지 않았다.</p>
</blockquote>
<h3 id="4-해결">4. 해결</h3>
<p>알고 보니 docker-compose로 down을 하면 docker-compose로 up(생성)한 컨테이너만 다운된다고 한다.</p>
<p>docker run으로 개별 컨테이너를 직접 생성한 경우 docker stop, docker rm으로만 제거가 가능하다.</p>
<p>팀원이 어제 배포하고 ec2에서 문제 해결하면서 cli에서 <code>docker run</code>으로 컨테이너를 직접 생성했는지, 그 컨테이너는 docker-compose down으로 삭제가 안 됐던 거다.</p>
<p>그래서 <code>deploy.sh</code>에 한 줄 추가해서 compose를 통하지 않고 직접 run한 web 컨테이너도 삭제하도록 했다.</p>
<pre><code># 기존 orphan 컨테이너 정리 
echo &quot;stop and remove old containers (with orphans)&quot;
sudo docker-compose -f /home/ubuntu/srv/ubuntu/docker-compose.prod.yml down --remove-orphans
📌sudo docker rm -f web || true📌</code></pre><p>(물론 실제 코드에 압정은 없다. 설명을 위해 표시한 거다.)</p>
<p>참고로 web은 컨테이너 이름이고, <code>docker-compose.prod.yml</code>에서 이름을 지정하고 있다.</p>
<h2 id="-docker-명령어">(+) Docker 명령어</h2>
<p>deploy.sh에 있던 명령어를 뜯어보자.</p>
<h3 id="기존-컨테이너-정리">기존 컨테이너 정리</h3>
<blockquote>
<p><code>sudo docker-compose -f /(생략)/docker-compose.prod.yml down --remove-orphans</code></p>
</blockquote>
<ul>
<li><p><code>sudo</code> : 관리자 권한으로 명령어 실행</p>
</li>
<li><p><code>docker-compose down</code> : docker 컨테이너를 종료(down)시킨다.</p>
<ul>
<li>(<code>docker-compose.yml</code> 또는 지정한 파일)에 정의된 서비스들의 컨테이너를 중지하고 제거</li>
</ul>
</li>
<li><p><code>-f /(생략)/docker-compose.prod.yml</code> : 파일 지정</p>
<ul>
<li>-f 옵션 없이 실행하면 <code>docker-compose.yml</code>에 정의된 컨테이너를 빌드한다.</li>
<li><code>docker-compose.prod.yml</code>를 빌드하려면 파일을 지정해야 한다.</li>
<li>로컬에서 개발할 때는 <code>docker-compose.yml</code>를, 서버에서는 <code>docker-compose.prod.yml</code>를 쓴다. product(제품)의 prod다.</li>
</ul>
</li>
<li><p><code>--remove-orphans</code> : (<code>docker-compose.yml</code> 또는 지정한 파일)에 정의되지 않은 서비스의 컨테이너도 함께 제거</p>
</li>
</ul>
<h3 id="새-컨테이너-실행">새 컨테이너 실행</h3>
<blockquote>
<p><code>sudo docker-compose -f /(생략)/docker-compose.prod.yml up --build -d</code></p>
</blockquote>
<ul>
<li><p><code>docker-compose up</code> : 이미지 빌드(필요시) + 컨테이너 생성 + 실행</p>
<ul>
<li>down은 컨테이너 종료였고, up은 실행</li>
</ul>
</li>
<li><p><code>--build</code> : 컨테이너 실행 전에 이미지를 새로 빌드</p>
<ul>
<li>코드가 변경되었을 때 새로 반영하기 위함</li>
<li><code>--build</code> 안 쓰면 기존 이미지로 컨테이너만 실행</li>
<li>up 대신 build를 쓰면 (<code>docker-compose build</code>) 이미지를 빌드만하고 실행하지 않음. 이 경우 별도로 <code>docker-compose up</code> 필요.</li>
</ul>
</li>
<li><p><code>-d</code> : 백그라운드에서 실행 (데몬 모드)</p>
<ul>
<li>터미널에서 분리되어 실행 (detached)</li>
<li>안 쓰면 터미널에 로그가 계속 출력되고, 터미널 종료하면 컨테이너도 종료</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백엔드] 서버 배포 시 자주 발생하는 문제들 (AWS EC2, Github Action, Docker, Django)]]></title>
            <link>https://velog.io/@2-pi-r/%EC%84%9C%EB%B2%84-%EB%B0%B0%ED%8F%AC-%EC%8B%9C-%EC%9E%90%EC%A3%BC-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EB%AC%B8%EC%A0%9C%EB%93%A4</link>
            <guid>https://velog.io/@2-pi-r/%EC%84%9C%EB%B2%84-%EB%B0%B0%ED%8F%AC-%EC%8B%9C-%EC%9E%90%EC%A3%BC-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EB%AC%B8%EC%A0%9C%EB%93%A4</guid>
            <pubDate>Mon, 04 Aug 2025 18:00:02 GMT</pubDate>
            <description><![CDATA[<p>해커톤 결선 기간이 절반 지나갔다. 한 달 동안 개발하면서 발생했던 서버 문제들을 정리해보려고 한다.</p>
<h1 id="개발-환경--백엔드">개발 환경  (백엔드)</h1>
<ul>
<li><code>Django</code>: Python 기반 웹 프레임워크<ul>
<li><code>Django REST framework</code>: RestFul API 개발을 위한 라이브러리</li>
<li><code>Django Channels</code>: WebSocket 통신을 위한 라이브러리</li>
<li><code>Daphne</code>: Django Channels를 지원하는 HTTP/WebSocket 서버</li>
</ul>
</li>
<li><code>Swagger</code>: RESTful API 설계, 문서화, 테스트를 위한 프레임워크</li>
<li><code>MySQL</code>: 관계형 데이터베이스</li>
<li><code>Docker</code>: 개발 환경 관리, 배포</li>
<li><code>AWS</code><ul>
<li><code>EC2</code>: 웹 서버 운영</li>
<li><code>RDS</code>: 데이터베이스 운영</li>
</ul>
</li>
<li><code>Github Action</code>: CI/CD 파이프라인 구축 <em>(Github의 develop 브랜치에 커밋이 되면 → AWS EC2에 자동 배포되도록)</em></li>
</ul>
<h1 id="서버-배포-시-자주-발생하는-문제들">서버 배포 시 자주 발생하는 문제들</h1>
<p>문제가 정말정말 자주 발생했다. 유형은 거의 정해져 있었다.</p>
<blockquote>
<p><strong>1. DB 연결 실패
2. Django 마이그레이션 파일 충돌 (DB 관련)
3. github action 실패</strong></p>
</blockquote>
<h2 id="1-db-연결-실패">1. DB 연결 실패</h2>
<h3 id="우리-팀이-db에-연결한-방법">우리 팀이 DB에 연결한 방법</h3>
<ul>
<li><strong>로컬</strong>에서 <strong>(docker의 db 컨테이너)</strong><ul>
<li><strong><code>docker-compose.yml</code></strong>를 보면</li>
<li>db 컨테이너에서 DB의 host, port 등을 지정하고</li>
<li>web 컨테이너에서 <code>environment:</code>에 <code>DATABASE_HOST: db</code> 등을 지정한다.</li>
</ul>
</li>
<li><strong>서버</strong>에서 <strong>(settings/prod.py + 환경변수)</strong><ul>
<li><strong><code>docker-compose.prod.yml</code></strong>에는 db 컨테이너 없이 web만 있다.</li>
<li>web 컨테이너에서 <code>environment:</code>에 DJANGO_SETTINGS_MODULE: <code>(생략).settings.prod</code>를 지정한다.</li>
<li>settings/prod.py에서 DB의 host, port 등을 환경변수로 지정한다.</li>
<li>환경변수는 .env 파일에 있는데, gitignore에 넣어두고 커밋하지 않는다.<h3 id="문제-및-원인">문제 및 원인</h3>
서버(EC2 인스턴스)에 환경변수가 없어서 DB 연결에 실패했다.
그렇다고 .env 파일을 커밋할 수는 없으므로
임시방편으로 배포할 때마다 prod.py를 수정해서 환경변수 대신 값을 하드코딩으로 넣었다.<h3 id="해결-방법">해결 방법</h3>
1) github 사이트에서 환경변수를 지정하고,
2) github action에서 .env 파일을 생성한다.<h4 id="1-환경변수-지정하기">1) 환경변수 지정하기</h4>
<img src="https://velog.velcdn.com/images/2-pi-r/post/9a55decb-6994-49da-9742-03c05f763d35/image.png" alt=""></li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/d756416e-8473-4a99-8e67-a929c86cc46b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/33e6b080-478d-477c-b370-5b3d4aded4a8/image.png" alt=""></p>
<p>수정(연필 모양) 버튼 누르면 보안상 텅 비어 보인다. 기존 환경변수 중 하나만 수정하고 싶어도 항상 전체 .env 파일을 지정해야 한다.</p>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/365d99e5-9d22-478b-972d-3afbbebed34d/image.png" alt=""></p>
<h4 id="2-env-파일-생성">2) .env 파일 생성</h4>
<p>github action의 workflow file을 수정해야 한다.</p>
<pre><code>      # Action Secret에 설정한 ENV_VARS 값을 .env 파일로 만듦
      - name: create env file
        run: |
          touch .env
          echo &quot;${{ secrets.ENV_VARS }}&quot; &gt;&gt; .env</code></pre><h2 id="2-django-마이그레이션-파일-충돌-db-관련">2. Django 마이그레이션 파일 충돌 (DB 관련)</h2>
<h3 id="문제-및-원인-1">문제 및 원인</h3>
<p>마이그레이션 파일끼리 충돌하거나, 파일 내역과 DB 상태가 달라서 생기는 문제다.</p>
<p>  MVP 개발 단계라 DB 구조(<code>models.py</code>) 수정할 일이 많아서, 거의 배포할 때마다 문제가 발생했다. 찾아보니 다들 고통받고 있었다.</p>
<ul>
<li>develop 브랜치에 내 작업 브랜치를 머지할 때<ul>
<li>보통 팀원이 그 사이에 push해둔 마이그레이션 파일들과 내 브랜치의 마이그레이션 파일이 충돌</li>
<li>파일명이 같으면 git에서 충돌이 나지만, 파일명이 다르면 충돌 없이 머지된다. 이러나 저러나 문제다. ① git 충돌이랑, ② Django 마이그레이션 파일 충돌은 별개이기 때문이다.</li>
<li>① 해결해서 머지하면 EC2 서버로 배포되지만, 서버에서 ② 문제가 발생했다. 내가 해결한 마이그레이션 파일들 상태와 서버 DB 상태가 달랐기 때문.</li>
</ul>
</li>
<li>develop에서 새 작업 브랜치를 파서, 내 로컬에서 개발을 시작할 때<ul>
<li>내 로컬 DB 상태와 마이그레이션 파일 상태가 달라서 충돌</li>
</ul>
</li>
</ul>
<h3 id="서버-상태">서버 상태</h3>
<p>  보통 이러면 docker 컨테이너가 다운된다. EC2 웹 서버에 web 컨테이너가 다운되어 있으므로, 웹 페이지에 접속하면 사이트에 연결할 수 없다고 뜬다.
  <img src="https://velog.velcdn.com/images/2-pi-r/post/07c87256-4867-4f55-935a-4797c3053510/image.png" alt=""></p>
<h3 id="해결-방법-1">해결 방법</h3>
<p>  <a href="https://velog.io/@2-pi-r/Django-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EC%B6%A9%EB%8F%8C-%ED%95%B4%EA%B2%B0">[Django] 마이그레이션 충돌 해결</a></p>
<h2 id="3-github-action-실패">3. Github Action 실패</h2>
<h3 id="문제">문제</h3>
<p>Github Action을 이용해서 Github의 develop 브랜치에 커밋이 되면 → AWS EC2에 자동 배포되도록 해두었다.</p>
<p>이 과정에서 배포에 실패할 수도 있다.</p>
<h3 id="로그-확인-방법">로그 확인 방법</h3>
<h4 id="github-사이트---action에서">Github 사이트 - Action에서</h4>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/c54dd2ef-43e1-4ee7-9f2f-7ebdcd959a6f/image.png" alt=""></p>
<h4 id="로그-확인-어디서-실패했는지">로그 확인 (어디서 실패했는지)</h4>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/8e67bd43-f99e-4651-a692-a73e0adcaa07/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/0504e273-da5e-47f4-98d5-e079a4065da5/image.png" alt=""></p>
<h4 id="-로그-보는-법">(+) 로그 보는 법</h4>
<p>workflow file을 보면 감이 올 것이다.
<img src="https://velog.velcdn.com/images/2-pi-r/post/e5de8d09-7bf6-4135-ad6f-5bcb0130f421/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/6734afb7-de61-41ec-8b37-7759fb2e4a4e/image.png" alt=""></p>
<p>file의 name들이 아래 화면의 표시한 부분에 보이는 것이다.
<img src="https://velog.velcdn.com/images/2-pi-r/post/9c582dac-23bb-41dd-9dcf-3f8b4898801a/image.png" alt=""></p>
<h3 id="해결-예시">해결 예시</h3>
<blockquote>
<p><a href="https://velog.io/@2-pi-r/Github-Action-Docker-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%B9%8C%EB%93%9C-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0">(다음 게시글) [Github Action] 기존 Docker 컨테이너 삭제 안 되는 문제 해결</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[윈도우에서 리눅스 사용하기]]></title>
            <link>https://velog.io/@2-pi-r/%EC%9C%88%EB%8F%84%EC%9A%B0%EC%97%90%EC%84%9C-%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@2-pi-r/%EC%9C%88%EB%8F%84%EC%9A%B0%EC%97%90%EC%84%9C-%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 23 Jul 2025 11:06:38 GMT</pubDate>
            <description><![CDATA[<h1 id="wsl란">WSL란?</h1>
<blockquote>
<p>&quot;별도의 가상 머신 또는 이중 부팅 없이 Windows 컴퓨터에서 Linux 환경을 실행할 수 있는 Windows의 기능&quot;     (내부적으로는 VM 기반이지만 사용자가 VM을 관리할 필요 없고, 보다 가벼움)</p>
</blockquote>
<p>WSL (Windows Subsystem for Linux)을 사용하면
Windows 컴퓨터에서도</p>
<ul>
<li><strong>Linux 배포판</strong>을 설치할 수 있음.<ul>
<li>예) Ubuntu, OpenSUSE, Kali, Debian, Arch Linux 등</li>
<li><strong>Ubuntu</strong>가 기본 설정임.</li>
<li><strong>다른 거 여러 개 설치할 수도 있고,</strong></li>
<li><strong>기본 배포판 다른 걸로 바꿀 수 있음.</strong></li>
</ul>
</li>
<li>Linux 사용할 수 있음.<ul>
<li>애플리케이션, 유틸리티, Bash 명령줄 도구 등</li>
</ul>
</li>
</ul>
<h1 id="방법">방법</h1>
<h2 id="1-wsl-ubuntu-설치">1. WSL, Ubuntu 설치</h2>
<hr>
<p><strong>WSL, Ubuntu 설치</strong> : cmd를 관리자 권한으로 열어서 아래 명령어 실행하면 끝!</p>
<pre><code>wsl --install</code></pre><ul>
<li>wsl 설치한 적 없으면 이 명령어 작동X (<a href="https://learn.microsoft.com/ko-kr/windows/wsl/install">공식 문서</a>)</li>
<li>Microsoft store에서 Ubuntu 따로 설치할 필요 없음.</li>
<li>Ubuntu는 wsl랑 같이 자동으로 설치되고, 다른 버전이나 다른 배포판은 추후 wsl 명령어로 설치할 수 있음.</li>
</ul>
<hr>
<p>아래 명령어로 <strong>모든 배포판의 실행 상태와 wsl 버전 확인</strong> 가능</p>
<pre><code>wsl -l -v</code></pre><p><img src="https://velog.velcdn.com/images/2-pi-r/post/99ba0b74-c4a3-454d-9f9d-fdc7eb9d160a/image.png" alt=""></p>
<ul>
<li><code>-l</code>: 설치된 모든 배포판 목록 ( * 표시: 기본 배포판)</li>
<li><code>-v</code>: wsl 버전 확인</li>
</ul>
<hr>
<p>난 확인했을 때 이미 <strong>wsl</strong> 버전2였는데, <strong>버전1이면 2로 바꿔주기</strong>.
(<a href="https://learn.microsoft.com/ko-kr/windows/wsl/setup/environment#set-up-your-linux-username-and-password">공식 문서</a>에서 버전2로 세팅하라고 함.)</p>
<pre><code>wsl --set-default-version 2</code></pre><hr>
<p>이유는 모르겠지만 나는 기본 배포판이 Ubuntu가 아니라 docker-desktop로 되어 있길래, <strong>기본 배포판도 바꿔줌.</strong></p>
<pre><code>wsl -s &lt;배포판 이름&gt;</code></pre><hr>
<p>Ubuntu <strong>다른 버전 설치</strong>하고 싶으면 아래 명령어 사용</p>
<pre><code>wsl --install -d &lt;배포판 이름&gt;</code></pre><p><strong>사용 가능한 배포판 목록</strong>은 아래 명령어로 확인 가능</p>
<pre><code>wsl --list --online</code></pre><blockquote>
<p>자세한 배포판 관리(목록 출력, 제거, 백업 등) 방법은 <a href="https://velog.io/@w00j00ng351/wsl-%EB%B0%B0%ED%8F%AC%ED%8C%90-%EA%B4%80%EB%A6%AC-%EB%AA%A9%EB%A1%9D-%ED%99%95%EC%9D%B8-%EC%8B%A4%ED%96%89-%EC%A2%85%EB%A3%8C-%EC%A0%9C%EA%B1%B0-%EB%B0%B1%EC%97%85-%EB%B6%88%EB%9F%AC%EC%98%A4%EA%B8%B0">여기</a> 참고</p>
</blockquote>
<h2 id="2-ubuntu-열어서-idpw-생성">2. Ubuntu 열어서 ID/PW 생성</h2>
<blockquote>
<p>요약:</p>
</blockquote>
<ul>
<li>Ubuntu 설치 시 재부팅 필요</li>
<li>윈도우 사용자 이름에 한국어 있으면 Ubuntu-22.04 설치<ul>
<li>(24.04버전부터는 설치 시 오류. 추후 해결될지도?)</li>
</ul>
</li>
</ul>
<p>(상황 설명: 앞에서 wsl로 Ubuntu 설치할 수 있어서 Microsoft store 쓸 필요 없다고 했는데, 나는 헤매다가 store에서 설치했음. 그러나 설치가 안 됨.)</p>
<p><strong>Ubuntu 설치 시 재부팅 필요</strong>하대서(<a href="https://learn.microsoft.com/ko-kr/windows/wsl/setup/environment#set-up-your-linux-username-and-password">공식 문서</a>), 재부팅했더니 앱 설치는 됨.</p>
<p>그런데 (ppt 같은 다른 앱 열듯이) Ubuntu 앱 열면 이런 식으로 안 열림.
<img src="https://velog.velcdn.com/images/2-pi-r/post/8038d1c2-e023-4066-b24a-c581c951e706/image.png" alt=""></p>
<p>찾아보니 윈도우 사용자 이름이 한국어라서 (정확히는 ASCII가 아닌 문자가 있으면) Ubuntu 설치(setup) 시 문제 발생.
(<a href="https://superuser.com/questions/1895417/how-to-fix-oobe-command-usr-lib-wsl-wsl-setup-failed-exiting-on-windows-10#comment2991282_1895427">출처: 이 분이 Ubuntu 깃허브 통해서 오류 제보해서 수정 되고 있는 것 같음. 커밋이 어제인 걸로 봐선 아직 배포는 안 된 듯.</a>)</p>
<p>Ubuntu (뒤에 아무것도 없는 것. 최신 버전) 이거랑
Ubuntu-24.04 (LTS버)부터 이런 에러 발생한다고 해서
(∴) Ubuntu-22.04 설치해서 해결.</p>
<p>앱 여니까 자동으로 setup 완료하고 ID/PW 설정하라고 뜸.</p>
<p>wsl에서 기본 배포판도 Ubuntu-22.04로 바꿔줬음.</p>
<h2 id="3-linux-사용-cmd에서">3. Linux 사용 (cmd에서)</h2>
<h3 id="3-1-실행">3-1. 실행</h3>
<blockquote>
<p>당연한 거지만, wsl는 Linux가 아니라 Windows 명령어기 때문에
wsl 사용해서 Linux 배포판 실행한 후 거기서 wsl 쓰면 command not found 뜸.</p>
</blockquote>
<p>cmd에서 아래 명령어로 <strong>기본 배포판 실행</strong></p>
<pre><code>wsl</code></pre><p>→ <strong>이제 linux 환경</strong>인 것.
(예: cd, ls 같은 것들 윈도우에선 안 되는데 이제 쓸 수 있음.)</p>
<hr>
<p><strong>기본 배포판을 어떤 사용자로 실행</strong>하려면</p>
<pre><code>wsl -u &lt;유저이름&gt;</code></pre><hr>
<p><strong>다른 배포판 실행</strong>하려면</p>
<pre><code>wsl -d &lt;배포판 이름&gt;</code></pre><h3 id="3-2-종료">3-2. 종료</h3>
<p><strong>특정 배포판을 종료</strong>하려면</p>
<pre><code>wsl -t &lt;배포판 이름&gt;</code></pre><hr>
<p><strong>모든 배포판을 종료</strong>하려면</p>
<pre><code>wsl --shutdown</code></pre><h3 id="3-3-상태-확인">3-3. 상태 확인</h3>
<p>모든 배포판의 상태와 wsl 버전을 확인하려면</p>
<pre><code>wsl -l -v</code></pre><p>원래 종료해도 기본 배포판 하나는 Running인가봄...?
<img src="https://velog.velcdn.com/images/2-pi-r/post/dc3fbb06-6685-4d18-baf8-4bee807fedf6/image.png" alt=""></p>
<h2 id="-wsl를-vs-code에-연결">(+) WSL를 vs code에 연결</h2>
<ul>
<li>vs code에서 WSL라는 확장 프로그램 설치 필요</li>
<li>자세한 건 <a href="https://cuffyluv.tistory.com/245">여기</a> 참고</li>
</ul>
<h1 id="참고자료">참고자료</h1>
<ul>
<li>블로그<ul>
<li><a href="https://velog.io/@w00j00ng351/wsl-%EB%B0%B0%ED%8F%AC%ED%8C%90-%EA%B4%80%EB%A6%AC-%EB%AA%A9%EB%A1%9D-%ED%99%95%EC%9D%B8-%EC%8B%A4%ED%96%89-%EC%A2%85%EB%A3%8C-%EC%A0%9C%EA%B1%B0-%EB%B0%B1%EC%97%85-%EB%B6%88%EB%9F%AC%EC%98%A4%EA%B8%B0">wsl 배포판 관리 (목록 확인, 실행, 종료, 제거, 백업, 불러오기)</a></li>
<li><a href="https://hy2on.tistory.com/148">Window11에 WSL 사용하여 Linux (Ubuntu) 설치</a></li>
<li><a href="https://cuffyluv.tistory.com/245">[Linux] Windows 11에서 WSL2 설치하고 VSCode 연동하기</a></li>
</ul>
</li>
<li>공식문서<ul>
<li><a href="https://learn.microsoft.com/ko-kr/windows/wsl/install">WSL 설치</a></li>
<li><a href="https://learn.microsoft.com/ko-kr/windows/wsl/setup/environment#set-up-your-linux-username-and-password">WSL 개발 환경 설정</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[캡스톤] Slither 개념, 사용 방법, 결과 분석]]></title>
            <link>https://velog.io/@2-pi-r/%EC%A1%B8%ED%94%84-%EC%8A%A4%ED%83%80%ED%8A%B8-%EA%B8%B0%EC%88%A0%EB%B8%94%EB%A1%9C%EA%B7%B8</link>
            <guid>https://velog.io/@2-pi-r/%EC%A1%B8%ED%94%84-%EC%8A%A4%ED%83%80%ED%8A%B8-%EA%B8%B0%EC%88%A0%EB%B8%94%EB%A1%9C%EA%B7%B8</guid>
            <pubDate>Mon, 02 Jun 2025 08:02:29 GMT</pubDate>
            <description><![CDATA[<p>나는 캡스톤 프로젝트를 진행하면서 스마트 컨트랙트의 보안 취약점을 탐지하기 위해 Slither를 사용했다.</p>
<h1 id="slither란">Slither란?</h1>
<blockquote>
<p>스마트 컨트랙트에서 보안 취약점을 검사할 수 있는 오픈 소스 도구
<a href="https://github.com/crytic/slither">https://github.com/crytic/slither</a>
<img src="https://velog.velcdn.com/images/2-pi-r/post/35afaf1b-e76e-4dcf-a7a8-8ff76c218b6d/image.png" alt=""></p>
</blockquote>
<ul>
<li><p>Python3로 작성된, <strong>Solidity &amp; Vyper 정적 분석 프레임워크</strong></p>
<ul>
<li><strong>Solidity, Vyper</strong>: 스마트 컨트랙트를 작성하는 데 사용되는 프로그래밍 언어</li>
<li><strong>정적 분석(static analysis)</strong>: 코드 실행 없이, 코드의 구문과 구조를 검사하는 방법</li>
</ul>
</li>
<li><p>기능:</p>
<ul>
<li>보안 취약점 탐지 (<a href="https://github.com/crytic/slither?tab=readme-ov-file#detectors">Detectors</a>)<ul>
<li>99가지 유형 탐지 가능</li>
<li>유형별 설명은 <a href="https://github.com/crytic/slither/wiki/Detector-Documentation">여기</a> 참고</li>
</ul>
</li>
<li>코드 이해를 위한 시각적인 요약 제공 (<a href="https://github.com/crytic/slither?tab=readme-ov-file#printers">Printers</a>)</li>
<li>사용자 정의 분석 프로토타입 지원 (<a href="https://github.com/crytic/slither?tab=readme-ov-file#tools">Tools</a>)</li>
</ul>
</li>
</ul>
<h1 id="사용-방법-보안-취약점-탐지">사용 방법 (보안 취약점 탐지)</h1>
<h2 id="1-slither-설치">1. Slither 설치</h2>
<pre><code>python3 -m pip install slither-analyzer</code></pre><h2 id="2-분석할-solidity-파일-준비">2. 분석할 Solidity 파일 준비</h2>
<p>내 목적은 캡스톤 프로젝트에 적용하기 전에 Slither를 간단히 사용해보는 거라서 아무 코드나 돌려보려고 한다.</p>
<p><a href="https://github.com/smartbugs/smartbugs-wild/tree/master">SmartBugs Wild Dataset</a>에 보안 취약점을 포함하고 있는, 이미 작성된 스마트 컨트랙트 코드가 있다. 여기서 <a href="https://github.com/smartbugs/smartbugs-wild/blob/master/contracts/0x0000000000027f6d87be8ade118d9ee56767d993.sol">이 데이터</a> 한 개를 이용했다.</p>
<h2 id="3-slither-실행">3. Slither 실행</h2>
<h3 id="3-1-command로-실행">3-1) command로 실행</h3>
<p>스마트 컨트랙트 프로젝트에 종속성이 있으면 아래 명령어를 쓰라고 한다. (출처: <a href="https://github.com/crytic/slither?tab=readme-ov-file#usage">공식 문서</a>)</p>
<pre><code>slither .</code></pre><p>하지만 나는 지금 스마트 컨트랙트 코드 한 개를 돌리려고 하는 거니까 아래 명령어를 사용했다.</p>
<pre><code>slither example.sol</code></pre><h4 id="i-solidity-compilersolc-버전-오류-해결">i) Solidity Compiler(solc) 버전 오류 해결</h4>
<p>그냥 실행했더니 에러가 떴다.
<img src="https://velog.velcdn.com/images/2-pi-r/post/b4dedc6b-d0fe-4d0b-be75-c6d2851487bc/image.png" alt=""></p>
<p>내가 2에서 다운받은 Solidity 코드의 버전은 0.4.23이고, 코드 내에서 0.4.x 버전의 Solidity 컴파일러를 사용하여 컴파일되어야 함을 명시하고 있다.</p>
<pre><code>pragma solidity ^0.4.0;</code></pre><p>하지만 에러 메시지를 보면 나한테 설치된 Solidity 컴파일러 버전은 0.8.30이기 때문에 컴파일에 실패한다.</p>
<p>해결하려면</p>
<ul>
<li><a href="https://github.com/ethereum/solidity/releases?after=v0.4.26&amp;page=8">여기</a>에서 알맞은 버전의 Solidity 컴파일러를 다운받고</li>
<li>command에 <code>--solc</code> 옵션으로 컴파일러 경로를 지정해주면 된다.<pre><code>slither --solc C:/solidity-windows/solc_0.4.25/solc.exe example.sol</code></pre>컴파일러 경로는 로컬에서 어디에 다운받는지에 따라 사람마다 다르다. 나중에 팀 프로젝트에서 사용할 때에는 코드에서 solc 경로를 매번 바꿔줘야 해서 불편했다. 이 문제는  solc 경로를 .env 파일에 환경변수로 추가하고, gitignore에 .env를 추가해서 해결했다.</li>
</ul>
<h4 id="ii-분석-결과-출력">ii) 분석 결과 출력</h4>
<ul>
<li><p>Compilation Warning/Error (컴파일 워닝/에러)</p>
<pre><code>INFO:Detectors:
Claimable.transferOwnership(address) (0x0000000000027f6d87be8ade118d9ee56767d993.sol#67-69) should emit an event for:
      - pendingOwner = newOwner (0x0000000000027f6d87be8ade118d9ee56767d993.sol#68)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#missing-events-access-control</code></pre></li>
<li><p>Detectors (보안 취약점)</p>
<ul>
<li>아래가 취약점 1개 내용이다. 이런 게 여러 개 있었다.<pre><code>INFO:Detectors:
Claimable.transferOwnership(address) (0x0000000000027f6d87be8ade118d9ee56767d993.sol#67-69) should emit an event for:
    - pendingOwner = newOwner (0x0000000000027f6d87be8ade118d9ee56767d993.sol#68)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#missing-events-access-control</code></pre></li>
</ul>
</li>
<li><p>요약</p>
<ul>
<li><code>(3 contracts</code> → 스마트 컨트랙트를 3개를<ul>
<li><em>2에서 다운받은 파일 까봤을 때 <code>contract</code> 구조체가 3개 있었음. 스마트 컨트랙트 코드 3개가 한 파일에 있었나봄.</em></li>
</ul>
</li>
<li><code>with 100 detectors)</code> → 100가지 유형의 취약점 검사기(detector)로 분석했고 <ul>
<li><em>깃허브에 Detectors 99가지만 나와있는데 왜 100개인지 모르겠음. 다른 팀원들도 100개로 나오던데.</em></li>
</ul>
</li>
<li><code>7 result(s) found</code> → 7개의 결과를 발견했다.<ul>
<li><em>위에서 Compilation Warning/Error 1개, Detectors 5개 출력했는데 왜 7개라는 건지는 모르겠음.</em><pre><code>INFO:Slither:0x0000000000027f6d87be8ade118d9ee56767d993.sol analyzed (3 contracts with 100 detectors), 7 result(s) found</code></pre></li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="iii---json-옵션">iii) <code>--json</code> 옵션</h4>
<blockquote>
<p>분석 결과를 json 파일로 저장</p>
</blockquote>
<p>json 파일의 전체 구조는 아래와 같다.</p>
<pre><code>{
&quot;success&quot;,   // 예시: true
&quot;error&quot;,     // 예시: null
&quot;results&quot; {
       &quot;detectors&quot;: [ {}, {}, ... ]    // 발견한 취약점들 정보
}</code></pre><p>위에서 <code>detectors</code>의 원소는 아래와 같다.</p>
<pre><code>{
        &quot;elements&quot; : [ {}, {}, ... ],

        &quot;description&quot;, // 발견한 취약점의 설명
        ..., 
        &quot;id&quot;,     // 발견한 취약점의 id
        &quot;check&quot;,  // detector (취약점 유형)
        &quot;impact&quot;, // 해당 유형의 impact
        &quot;confidence&quot; // 해당 유형의 confidence
}</code></pre><p>위에서 <code>elements</code>의 원소는 아래와 같다.</p>
<pre><code>{
        &quot;type&quot;,
        &quot;name&quot;,
        &quot;source_mapping&quot;: {...},
        &quot;type_specific_fields&quot;: {
                                &quot;parent&quot;: {...},  // {} 안은 &quot;element&quot;와 동일
                                &quot;signature&quot;       // 없을 수도 있음.
                                                        }
}</code></pre><h4 id="iv---print-옵션">iv) <code>--print</code> 옵션</h4>
<blockquote>
<p>분석한 코드의 다양한 정보를 출력</p>
</blockquote>
<ul>
<li><a href="https://github.com/crytic/slither/wiki/Printer-documentation">Slither 공식 문서의 Printer 부분</a> 참고</li>
<li>ex) <pre><code>slither --print contract-summary example.sol
slither --print data-dependency example.sol</code></pre>나는 해보지는 않았다.</li>
</ul>
<h3 id="3-2-python-api로-실행">3-2) Python API로 실행</h3>
<ul>
<li><a href="https://github.com/crytic/slither/wiki/Python-API">Slither 공식 문서의 Python API 부분</a> 참고</li>
<li>이런 식으로 실행할 수도 있다고 한다. 나는 해보지는 않았다.</li>
</ul>
<pre><code>from slither import Slither

# solidity file로 Slither Object 생성
slither = Slither(&#39;example.sol&#39;)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 1단계] 6. 심층 신경망]]></title>
            <link>https://velog.io/@2-pi-r/%EB%94%A5%EB%9F%AC%EB%8B%9D-1%EB%8B%A8%EA%B3%84-6.-%EC%8B%AC%EC%B8%B5-%EC%8B%A0%EA%B2%BD%EB%A7%9D</link>
            <guid>https://velog.io/@2-pi-r/%EB%94%A5%EB%9F%AC%EB%8B%9D-1%EB%8B%A8%EA%B3%84-6.-%EC%8B%AC%EC%B8%B5-%EC%8B%A0%EA%B2%BD%EB%A7%9D</guid>
            <pubDate>Wed, 10 Apr 2024 13:44:40 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.boostcourse.org/ai215/lecture/355381/?isDesc=false">강의</a></p>
<h2 id="얕은-신경망-vs-심층-신경망">얕은 신경망 vs. 심층 신경망</h2>
<hr>
<ul>
<li><p>정의 :</p>
<ul>
<li>층이 얕은지 vs 깊은지 (은닉층 多)</li>
</ul>
</li>
<li><p>비교 :</p>
<ul>
<li>심층 신경망이 더 잘 작동함.</li>
</ul>
</li>
<li><p>이유 :</p>
<ul>
<li>같은 성능을 내려면, 얕은 신경망의 경우 훨씬 많은 노드가 필요함. (기하급수적)</li>
<li>복잡한 특징도 탐지 가능
 <img src="https://velog.velcdn.com/images/2-pi-r/post/023724e1-dbca-4ebc-ae3e-fd17e7abdd7a/image.png" alt=""><ul>
<li>낮은 층 : 간단한 특징을 탐지</li>
<li>깊은 층 : 그 간단한 특징을 모아서 복잡한 특징을 탐지
<img src="https://velog.velcdn.com/images/2-pi-r/post/b7f7ef11-5a5b-4a67-856e-5c7a7fb8cbd1/image.png" alt=""></li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 1단계] 5. 변수 vs. 하이퍼 파라미터]]></title>
            <link>https://velog.io/@2-pi-r/%EB%94%A5%EB%9F%AC%EB%8B%9D-1%EB%8B%A8%EA%B3%84-5.-%EB%B3%80%EC%88%98-vs.-%ED%95%98%EC%9D%B4%ED%8D%BC-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0</link>
            <guid>https://velog.io/@2-pi-r/%EB%94%A5%EB%9F%AC%EB%8B%9D-1%EB%8B%A8%EA%B3%84-5.-%EB%B3%80%EC%88%98-vs.-%ED%95%98%EC%9D%B4%ED%8D%BC-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0</guid>
            <pubDate>Wed, 10 Apr 2024 13:40:03 GMT</pubDate>
            <description><![CDATA[<blockquote>
</blockquote>
<ul>
<li><strong>변수</strong> : 신경망에서 학습 대상<ul>
<li>$w$, $b$</li>
</ul>
</li>
<li><strong>하이퍼 파라미터</strong> : 학습 알고리즘에게 우리가 정해줘야 하는 것<ul>
<li>learning rate $α$</li>
<li>반복 <em>iteration</em> 횟수</li>
<li>은닉층 <em>hidden layer</em> 개수 $L$</li>
<li>은닉유닛 <em>hidden units</em> 개수 $n^{[l]}$</li>
<li>활성화 함수 선택 $a^{[l]}$</li>
<li>모멘텀 항 <em>momentum term</em></li>
<li>미니배치 크기 <em>mini batch size</em></li>
</ul>
</li>
</ul>
<h2 id="파라미터변수-초기화">파라미터(변수) 초기화</h2>
<hr>
<blockquote>
</blockquote>
<ul>
<li><p>학습 전 파라미터를 초기화한다.</p>
<ul>
<li>w = np.random.rand( <code>shape</code> ) * 0.01
→ 랜덤 초기화.</li>
<li>b = np.zeros( <code>shape</code> )
→ 0으로 초기화</li>
</ul>
</li>
<li><p>이유</p>
<ul>
<li>$$w$$<ul>
<li>w를 모두 0으로 초기화하면 안 됨. 같은 층의 모든 유닛이 언제나 같은 값을 계산하기 때문에. → 랜덤하게 초기화.</li>
<li>0.01을 곱해서 아주 작은 수로 만들어줌. (0.01 말고 다른 수여도 괜찮음.)
← w가 크면 $z=w^Tx+b$가 너무 크거나 작고, 그러면 sigmoid, tanh 활성함수를 거쳤을 때 도함수가 거의 0이라 학습 속도가 너무 느려지기 때문.</li>
</ul>
</li>
<li>$$b$$<ul>
<li>b는 모두 0으로 초기화해도 이런 문제가 생기지 않음. → 그냥 0으로 초기화.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="하이퍼-파라미터매개변수-초기화">하이퍼 파라미터(매개변수) 초기화</h2>
<hr>
<ul>
<li>방법<ul>
<li>매우 경험적임</li>
<li>하나로 정하고 → 여러 번 시도 → 비용함수 $J$값 보면서 조정</li>
</ul>
</li>
<li>조언<ul>
<li>몇 년씩 하는 장기 프로젝트라면 → 몇 달마다 점검하기.</li>
<li>왜냐면 지금 최적인 걸로 정했어도, 나중에 최적인 값이 바뀔 수도 있음.
CPU, GPU, 네트워크, 데이터가 달라지니까.</li>
</ul>
</li>
</ul>
<h3 id="learning-rate">learning rate</h3>
<blockquote>
</blockquote>
<ul>
<li>learning rate 정하기<ul>
<li>일단 <strong>0.01</strong>로 시작 (?)</li>
</ul>
</li>
</ul>
<h3 id="활성화-함수">활성화 함수</h3>
<blockquote>
<p>활성화 함수 정하기</p>
</blockquote>
<ul>
<li>데이터 값의 범위가 (거의) 양수일 때 → <strong>ReLU</strong></li>
<li>대체로 sigmoid냐 tanh냐 중에선 → <strong>tanh</strong></li>
<li>Binary classification의 출력층 → <strong>sigmoid</strong></li>
</ul>
<ul>
<li><p>비선형이어야 함.</p>
</li>
<li><p>종류 (4가지)</p>
<blockquote>
<p><strong>sigmoid</strong>
<img src="https://velog.velcdn.com/images/2-pi-r/post/261492fa-01c3-45e4-9802-8550053473de/image.png" alt="">
<em>[장점]</em>
  출력값 : 0<del>1 → binary classification의 출력층에 적합.
  왜냐하면 그 출력층은 y=1일 확률을 나타내는데, 확률은 0</del>1 사이 값이니까.</p>
</blockquote>
<blockquote>
<p><strong>tanh</strong>
<img src="https://velog.velcdn.com/images/2-pi-r/post/b91d47de-5abd-49eb-bca4-e176ad850b8d/image.png" alt="">
<em>[장점]</em>
  평균이 0
  → 데이터를 원점으로 이동하는 효과 有.
  → 평균이 0.5인 Sigmoid보다 효율적.</p>
</blockquote>
<blockquote>
<p><strong>ReLU</strong>
<img src="https://velog.velcdn.com/images/2-pi-r/post/64572e4b-6255-41d2-b8a9-33753fc41b2f/image.png" alt="">
<em>[장점]</em>
  sigmoid, tanh는 x가 너무 크거나 작을 때 도함수가 거의 0
  → 학습 속도 저하
  반면 ReLU는 x 절반<code>(x&gt;0)</code>은 도함수가 1으로, 0에 가깝지 X
  → 학습 빠름.</p>
</blockquote>
<blockquote>
<p><strong>leaky ReLU</strong>
<img src="https://velog.velcdn.com/images/2-pi-r/post/c6c2ef62-8918-4bd8-9039-ff0e053a100a/image.png" alt="">
  <em>[장점]</em>
  남은 x 절반(x&lt;0)도 도함수가 0이 아님. → 학습 더 빠름.</p>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 1단계] 4. 신경망 학습 (딥러닝)]]></title>
            <link>https://velog.io/@2-pi-r/%EB%94%A5%EB%9F%AC%EB%8B%9D-1%EB%8B%A8%EA%B3%84-4.-%EC%8B%A0%EA%B2%BD%EB%A7%9D-%ED%95%99%EC%8A%B5-%EB%94%A5%EB%9F%AC%EB%8B%9D</link>
            <guid>https://velog.io/@2-pi-r/%EB%94%A5%EB%9F%AC%EB%8B%9D-1%EB%8B%A8%EA%B3%84-4.-%EC%8B%A0%EA%B2%BD%EB%A7%9D-%ED%95%99%EC%8A%B5-%EB%94%A5%EB%9F%AC%EB%8B%9D</guid>
            <pubDate>Wed, 10 Apr 2024 13:15:53 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.boostcourse.org/ai215/lecture/34819?isDesc=false">강의</a></p>
<blockquote>
</blockquote>
<ul>
<li>학습 목표 :<ul>
<li><code>cost 함수</code> $J$의 값이 최소가 되도록 하는 <strong>w와 b를 찾는 것</strong></li>
</ul>
</li>
<li>방법 :<ul>
<li><strong>경사하강법</strong> 이용</li>
</ul>
</li>
<li>학습 과정 (의사 코드) : <pre><code>  for 반복 횟수
      for 모든 데이터
          1. **순전파** (Forward pass) `→ predict`
            2. **역전파** (Backward pass) `→ 도함수 계산`
            3. `가중치 업데이트`

</code></pre></li>
</ul>
<h2 id="흐름">흐름</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/336dc624-7bb6-4e69-8fe7-877f2473d5a0/image.png" alt=""></p>
<ul>
<li><p>표 부연설명</p>
<ul>
<li>$a^{[l-1]}$  : level $(l-1)$의 $a = σ(z)$</li>
<li>정전파 → $a$ 계산
역전파 → $da$ 계산 ($a$를 미분한 것)</li>
</ul>
</li>
<li><p>순전파/역전파 구현에서</p>
<ul>
<li>캐시(cache)는 순전파에서의 변수를 → 해당하는 역전파 단계에 전달한다.</li>
<li>output인 $a$ 말고도 $z$, $w$, $b$를 전달하면 미분값을 계산할 때 유용하기 때문이다.</li>
</ul>
</li>
</ul>
<h2 id="디버깅--행렬-shape을-확인하자">디버깅 : 행렬 shape을 확인하자</h2>
<hr>
<blockquote>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/c52cd660-a7c7-49fa-9fa9-f4aaa9243200/image.png" alt=""></p>
</blockquote>
<ul>
<li>설명
  <img src="https://velog.velcdn.com/images/2-pi-r/post/918e6fc7-4b57-4897-bb0c-6f4f1584b1de/image.png" alt=""></li>
</ul>
<ul>
<li>벡터화<ul>
<li>$w$, $b$는 모든 데이터에 대해 공통이므로 그대로.</li>
<li>$Z$, $X$만 바뀜.</li>
<li>$b$는 그대로긴 한데 $(n^{[1]}, m)$으로 브로드캐스팅된 후 더해짐.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 1단계] 3. 파이썬에서의 벡터화]]></title>
            <link>https://velog.io/@2-pi-r/%EB%94%A5%EB%9F%AC%EB%8B%9D-1%EB%8B%A8%EA%B3%84-3.-%ED%8C%8C%EC%9D%B4%EC%8D%AC%EC%97%90%EC%84%9C%EC%9D%98-%EB%B2%A1%ED%84%B0%ED%99%94</link>
            <guid>https://velog.io/@2-pi-r/%EB%94%A5%EB%9F%AC%EB%8B%9D-1%EB%8B%A8%EA%B3%84-3.-%ED%8C%8C%EC%9D%B4%EC%8D%AC%EC%97%90%EC%84%9C%EC%9D%98-%EB%B2%A1%ED%84%B0%ED%99%94</guid>
            <pubDate>Sat, 23 Mar 2024 06:09:47 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.boostcourse.org/ai215/lecture/355363?isDesc=false">강의</a></p>
<h2 id="벡터화-vectorization">벡터화 (Vectorization)</h2>
<hr>
<ul>
<li><p>정의</p>
<ul>
<li>for문 대신 벡터 연산을 이용하여 효율적으로 계산하는 방법</li>
</ul>
</li>
<li><p>필요성</p>
<ul>
<li>코드에서 for문 써서 n번 연산할 것을, 벡터 써서 코드 1줄로 해결 가능.
⇒ 연산 빨라짐.
⇒ 딥러닝에서 큰 데이터셋 다룰 때 유리.</li>
</ul>
</li>
<li><p>ex) 뉴런 하나를 벡터화</p>
<ul>
<li><p>상황</p>
<ul>
<li>데이터 1개, 반복횟수 1번
→ $z = W^TX + b$ 를 계산하는 방법<ul>
<li>$ŷ = σ(z)$ 할 때 그 z 맞음.</li>
</ul>
</li>
</ul>
</li>
<li><p><em>Non-vectorization</em></p>
<pre><code class="language-python">  z = 0
  for i in range(n-x):
      z += w[i] * x[i]
  z += b</code></pre>
</li>
</ul>
</li>
</ul>
<pre><code>- *Vectorization*

    ```python
    import numpy as np

    z = np.dox( np.transpose(W), X) + b
    ```</code></pre><ul>
<li>ex) Layer 한 층을 벡터화
  <img src="https://velog.velcdn.com/images/2-pi-r/post/0b0bfedc-ccba-44c4-9c24-c784035f21f8/image.png" alt=""></li>
</ul>
<blockquote>
</blockquote>
<ul>
<li>되도록 for문 쓰지 말고,
대신 numpy 내장함수를 이용할 수 있나 확인하기! (<strong>벡터화</strong>)</li>
</ul>
<h2 id="브로드캐스팅-of-파이썬">브로드캐스팅 of 파이썬</h2>
<hr>
<ul>
<li>정의 :<ul>
<li>차원이 다른 행렬끼리 계산할 수 있도록 자동으로 차원을 변환한 후 계산해줌.</li>
</ul>
</li>
<li>방법 :<ul>
<li>직관적임.</li>
</ul>
</li>
</ul>
<h2 id="numpy-쓸-때-오류-줄이는-tip">numpy 쓸 때 오류 줄이는 Tip!</h2>
<hr>
<ul>
<li>reshape을 생활화하자.<ul>
<li>shape을 확실히 아는 게 아니면 그냥 한 번 더 써주자. 이런 건 확실히 하는 게 좋음.</li>
<li>게다가 비용도 적은데 왜 안 써? <code>cost = O(n)</code></li>
</ul>
</li>
<li>shape이 헷갈리면 assert(<code>a.shape == (1,5)</code>) 함수로 찍어보자.<ul>
<li>확인은 자주자주!</li>
</ul>
</li>
<li>rank가 1인 배열은 아예 사용하지 말자.<ul>
<li>shape이 <code>(4,)</code>처럼 <code>(n,)</code> 꼴인 배열.</li>
<li>행 벡터도, 열 벡터도 아님. ⇒ 직관적이지 않은 결과가 나올 수 있음.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 1단계] 2. 로지스틱 회귀]]></title>
            <link>https://velog.io/@2-pi-r/%EB%94%A5%EB%9F%AC%EB%8B%9D-1%EB%8B%A8%EA%B3%84-2.-%EB%A1%9C%EC%A7%80%EC%8A%A4%ED%8B%B1-%ED%9A%8C%EA%B7%80-%EC%8B%A0%EA%B2%BD%EB%A7%9D-%ED%95%99%EC%8A%B5</link>
            <guid>https://velog.io/@2-pi-r/%EB%94%A5%EB%9F%AC%EB%8B%9D-1%EB%8B%A8%EA%B3%84-2.-%EB%A1%9C%EC%A7%80%EC%8A%A4%ED%8B%B1-%ED%9A%8C%EA%B7%80-%EC%8B%A0%EA%B2%BD%EB%A7%9D-%ED%95%99%EC%8A%B5</guid>
            <pubDate>Sun, 17 Mar 2024 06:59:34 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.boostcourse.org/ai215/lecture/355348?isDesc=false">강의</a></p>
<h2 id="로지스틱-회귀-logistic-regression">로지스틱 회귀 (Logistic Regression)</h2>
<hr>
<ul>
<li><p>정의</p>
<ul>
<li><p>주로 Binary Classification 문제에 사용되는, 통계학적인 머신러닝 알고리즘</p>
</li>
<li><p>입력 변수의 Linear Combination 을 로지스틱 함수에 적용하여 확률을 계산하고, 이를 기반으로 Binary Classification를 수행</p>
<ul>
<li><strong>Linear Combination</strong> : 주어진 변수들을 상수와 가중치의 곱으로 더한 것</li>
<li><strong>Binary Classification</strong> : 0 or 1로 분류</li>
</ul>
</li>
<li><p>그림 ex)</p>
<ul>
<li><p>로지스틱 회귀
<img src="https://velog.velcdn.com/images/2-pi-r/post/4e7cf5d2-7e9a-4298-b0a2-0fd78a1582fb/image.png" alt=""></p>
<ul>
<li>신경망 모델 (여러 층이 있음.)
<img src="https://velog.velcdn.com/images/2-pi-r/post/7e738a22-c56a-4aae-b0e2-f24b0bb01bd8/image.png" alt=""></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<ul>
<li><strong>선형 회귀</strong> (Linear Regression)<ul>
<li>예측을 위한 직선(선형 방정식)을 학습</li>
</ul>
</li>
</ul>
<p>$$
ŷ = W^T X+b 
$$</p>
<ul>
<li><strong>sigmoid 함수</strong><ul>
<li>정의
  <img src="https://velog.velcdn.com/images/2-pi-r/post/7ef63562-300a-49aa-9451-d857a223485f/image.png" alt=""></li>
</ul>
</li>
</ul>
<pre><code>- Logistic Regression + Binary Classification에서 ŷ는 `y=1일 확률`을 의미 → 따라서 결과값을 0~1로 바꿔주기 위해 sigmoid를 씌움

    $$
    ŷ =σ(W^T X+b) 
    $$</code></pre><ul>
<li><p>오차$(y-ŷ)$ 계산하기</p>
<ul>
<li><p><strong>Loss 함수</strong> : <em>한</em> 입력 data에서의 오차
  $$
  L(ŷ,y)
  $$</p>
<ul>
<li>구체적인 수식
<img src="https://velog.velcdn.com/images/2-pi-r/post/d4d5bd08-92ac-4073-a213-a2b1af3d82ca/image.jpg" alt=""></li>
</ul>
</li>
<li><p><strong>Cost 함수</strong> : <em>전체</em> 입력 data에서의 오차</p>
<ul>
<li>Loss 함수의 평균 
$$
J(w,b)
$$</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 1단계] 1. 개념]]></title>
            <link>https://velog.io/@2-pi-r/%EB%94%A5%EB%9F%AC%EB%8B%9D-1%EB%8B%A8%EA%B3%84-1.-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@2-pi-r/%EB%94%A5%EB%9F%AC%EB%8B%9D-1%EB%8B%A8%EA%B3%84-1.-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Sun, 17 Mar 2024 06:52:13 GMT</pubDate>
            <description><![CDATA[<p><strong><a href="https://www.boostcourse.org/ai215/joinLectures/86249?isDesc=false">강의</a></strong></p>
<h2 id="인공지능-vs-머신러닝-vs-딥러닝"><strong>인공지능 vs 머신러닝 vs 딥러닝</strong></h2>
<hr>
<ul>
<li>인공지능 ⊃ 머신러닝 ⊃ 딥러닝</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li><p>정의</p>
<ul>
<li><strong>인공지능</strong> : 인간의 지적 능력을 컴퓨터에서 구현하는 기술</li>
<li><strong>머신러닝</strong> : 컴퓨터를 인간처럼 학습시킴으로써 인간의 도움 없이 컴퓨터가 스스로 새로운 규칙을 생성할 수 있지 않을까 하는 발상으로부터 시작 / 데이터에서 패턴을 스스로 찾아내고 이를 기반으로 다른 데이터가 주어졌을 때 예측하는 기술</li>
<li><strong>딥러닝</strong> : 신경망을 이용한 머신러닝 방법 / 신경망을 학습시키는 것</li>
</ul>
</li>
<li><p><a href="https://tcpschool.com/deep2018/deep2018_ai_vs">참고자료</a></p>
</li>
</ul>
<h2 id="머신러닝">머신러닝</h2>
<hr>
<blockquote>
</blockquote>
<ul>
<li>종류<ul>
<li><strong>지도 학습</strong> (Supervised Learning)</li>
<li><strong>비지도 학습</strong> (Unsupervised Learning)</li>
<li><strong>강화 학습</strong> (Reinforcement Learning)</li>
</ul>
</li>
</ul>
<h3 id="지도-학습">지도 학습</h3>
<ul>
<li>label(정답)이 붙은 데이터로 학습하는 것</li>
</ul>
<h3 id="비지도-학습">비지도 학습</h3>
<ul>
<li>label이 없는 데이터로 학습하는 것</li>
<li>종류 : <strong>군집화</strong></li>
</ul>
<h3 id="강화-학습">강화 학습</h3>
<ul>
<li>차이점 :<ul>
<li>지도 학습/비지도 학습 → 데이터가 주어진 상태 + 변화가 없는 정적인 환경에서 학습</li>
<li>강화 학습 → 어떤 환경 안에서 정의된 주체(agent)가 현재의 상태(state)를 관찰하여 선택할 수 있는 행동(action)들 중에서 가장 최대의 보상(reward)을 가져다주는 행동이 무엇인지를 학습</li>
</ul>
</li>
</ul>
<h2 id="딥러닝">딥러닝</h2>
<hr>
<blockquote>
</blockquote>
<ul>
<li>정의<ul>
<li><strong>딥러닝</strong> : 신경망을 학습시키는 것</li>
<li><strong>신경망</strong> : 뉴런이 복잡하게 연결된 신경망을 컴퓨터에서 인공적으로 구현한 것 / 입력(x)와 출력(y)를 매칭해주는 함수를 찾는 과정</li>
<li><strong>뉴런</strong> = <strong>퍼셉트론</strong> : 신경망의 기본 단위</li>
</ul>
</li>
</ul>
<h3 id="신경망">신경망</h3>
<ul>
<li>용어<ul>
<li>신경망
= 인공신경망
= <strong>ANN</strong> (Artificial Neural Network)</li>
</ul>
</li>
<li>종류 : (아래 모두 지도학습으로 학습시킬 수 있음.)</li>
</ul>
<pre><code>| Standard NN | ~ Neural Network |  |
| --- | --- | --- |
| CNN | 합성곱 신경망 Convolutional Neural Network | 이미지 |
| RNN | 순환 신경망 Recurrent Neural Network | 오디오, 언어 (1차원 시퀀스 데이터) |
| Custom/Hybrid 신경망 |  |  |</code></pre><ul>
<li><p>간단한 구조
  <img src="https://velog.velcdn.com/images/2-pi-r/post/a08fffe6-8c53-4676-b071-05bb0647c3b8/image.png" alt=""></p>
<ul>
<li><p>Output Layer</p>
<ul>
<li>노란 형광펜 (X)</li>
<li>파란 형광펜 (O)</li>
</ul>
</li>
<li><p>Layer 수 셀 때 → Input Layer는 빼고 셈.</p>
<ul>
<li>Input Layer만
node로 표현하지도 않고,
w, b도 없음.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="데이터">데이터</h2>
<hr>
<ul>
<li>종류<ul>
<li><strong>정형 데이터</strong> (Structured data) : 각 데이터의 feature을 표 형태로 나타낼 수 있는 데이터</li>
<li><strong>비정형 데이터</strong> (Un~ data) : 나머지. ex) 이미지, 텍스트, 오디오</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝] 강의 수강 계획]]></title>
            <link>https://velog.io/@2-pi-r/%EB%94%A5%EB%9F%AC%EB%8B%9D-1%EB%8B%A8%EA%B3%84-%EA%B0%95%EC%9D%98-%ED%95%84%EA%B8%B0</link>
            <guid>https://velog.io/@2-pi-r/%EB%94%A5%EB%9F%AC%EB%8B%9D-1%EB%8B%A8%EA%B3%84-%EA%B0%95%EC%9D%98-%ED%95%84%EA%B8%B0</guid>
            <pubDate>Sun, 17 Mar 2024 06:45:38 GMT</pubDate>
            <description><![CDATA[<p>이번 학기에 이화여대 인공지능 동아리 Euron에 합격했다.</p>
<p>나는 중급 세션에서 활동한다.</p>
<ul>
<li>대상: 머신러닝에 관한 기본 이해가 있는 사람</li>
<li>분야: 딥러닝, CV, NLP</li>
</ul>
<p>수강 계획은 다음과 같다.</p>
<ol>
<li>부스트코스의 <a href="https://www.boostcourse.org/ai215/joinLectures/86246">딥러닝 1단계: 신경망과 딥러닝</a></li>
<li>부스트코스의 <a href="https://www.boostcourse.org/ai216/lecture/500424">딥러닝 2단계: 심층 신경망 성능 향상시키기</a></li>
<li>부스트코스의 <a href="https://www.boostcourse.org/ai218/joinLectures/20025?isDesc=false">딥러닝 4단계: 합성곱 신경망 네트워크(CNN)</a> </li>
<li>부스트코스의 <a href="https://www.boostcourse.org/ai330/joinLectures/370179?null">자연어 처리의 모든 것</a></li>
</ol>
<p>한 학기 동안 매주 강의를 듣고 퀴즈를 풀며 배운 점을 기록하려고 한다.</p>
<p>(+) 필기 목차와 강의 목차가 완전히 다르다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android+Firebase] 앱에 알림 추가]]></title>
            <link>https://velog.io/@2-pi-r/AndroidFirebase-%EC%95%B1%EC%97%90-%EC%95%8C%EB%A6%BC-%EC%B6%94%EA%B0%80</link>
            <guid>https://velog.io/@2-pi-r/AndroidFirebase-%EC%95%B1%EC%97%90-%EC%95%8C%EB%A6%BC-%EC%B6%94%EA%B0%80</guid>
            <pubDate>Wed, 03 Jan 2024 11:32:06 GMT</pubDate>
            <description><![CDATA[<h2 id="참고자료">참고자료</h2>
<ul>
<li><a href="https://developer.android.com/develop/ui/views/notifications/build-notification?hl=ko">앱에 알림 추가 - 알림 만들기</a><ul>
<li>안드로이드 공식 문서. &#39;앱에 알림 추가&#39; 시리즈의 다른 글도 참고할 만 하다.</li>
</ul>
</li>
</ul>
<h2 id="작성중">작성중...</h2>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android+Firebase] 로그인 없이 Firebase Authentication (익명 로그인)]]></title>
            <link>https://velog.io/@2-pi-r/AndroidFirebase-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%B3%B4%EC%95%88%EA%B7%9C%EC%B9%99</link>
            <guid>https://velog.io/@2-pi-r/AndroidFirebase-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%B3%B4%EC%95%88%EA%B7%9C%EC%B9%99</guid>
            <pubDate>Tue, 02 Jan 2024 15:57:09 GMT</pubDate>
            <description><![CDATA[<p>읽기 규칙을 수정해야 한다.</p>
<p>사용자를 식별할 필요가 없으므로 로그인은 필요 없다. 앱을 통해서 접속했다면 모든 사용자를 허용하는 걸로 바꾸고 싶다....만 찾아보니 그렇게는 힘든 것 같았다.</p>
<p>그래서 찾은 방법이 익명 로그인.</p>
<h2 id="참고자료">참고자료</h2>
<ul>
<li><a href="https://todaycode.tistory.com/19">다른 사람 블로그</a><ul>
<li>거의 이것만 보고 작성함.</li>
</ul>
</li>
<li><a href="https://firebase.google.com/docs/auth/android/anonymous-auth?hl=ko#java">Android에서 익명으로 Firebase에 인증</a><ul>
<li>공식 문서.</li>
</ul>
</li>
<li><a href="https://firebase.google.com/docs/auth/android/start?hl=ko#java">Android에서 Firebase 인증 시작하기</a><ul>
<li>공식 문서. 위의 자료를 이해하기 위해 활용.</li>
<li><img src="https://velog.velcdn.com/images/2-pi-r/post/33366b0e-783a-4668-a106-51f53d058f20/image.png" alt=""> </li>
<li>만약 로그인되어 있으면 user 정보가, 로그인 전이면 null이 반환된다.</li>
</ul>
</li>
</ul>
<h2 id="내-코드">내 코드</h2>
<h3 id="백업-규칙">백업 규칙</h3>
<pre><code>{
  &quot;rules&quot;: {
    &quot;.read&quot;: &quot;auth != null&quot;,
    &quot;.write&quot;: false
  }
}</code></pre><h3 id="mainactivityjava">MainActivity.java</h3>
<pre><code>import androidx.annotation.NonNull;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.auth.AuthResult;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;

...

    protected void onCreate(Bundle savedInstanceState) {
        ...


        // Firebase 익명 로그인
        mAuth = FirebaseAuth.getInstance(); // Firebase Auth 초기화
        FirebaseUser user = mAuth.getCurrentUser();
        if(user == null) { // 아직 계정 생성 안 된 사용자
            signInAnonymously(); // 계정 생성
        }


        // DB 연결 후 key 값 읽어와서 화면에 출력
        FirebaseApp.initializeApp(this);
        ...
        mReference.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                ...
                // 데이터 읽어와서 화면에 출력하는 부분
                ...
                System.out.println(&quot;정상: DB 연결 성공&quot;);
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                System.out.println(&quot;에러: DB 연결 실패&quot;);
            }
        });
    }


    private void signInAnonymously() { // 익명 계정 생성
        mAuth.signInAnonymously().addOnCompleteListener(this, new OnCompleteListener&lt;AuthResult&gt;() {
            @Override
            public void onComplete(@NonNull Task&lt;AuthResult&gt; task) {
                if (task.isSuccessful()) {
                    System.out.println(&quot;정상: 익명 로그인 성공&quot;);
                } else {
                    System.out.println(&quot;에러: 익명 로그인 실패&quot;);
                }
            }
        });
    }
</code></pre><p>여기서 <code>// DB 연결 후 key 값 읽어와서 화면에 출력</code> 이 부분은 참고를 위해 넣은 것으로, (익명 로그인 작업 전에 작성한) DB 읽어오는 게시글 내용이다.</p>
<h3 id="activity_mainxml">activity_main.xml</h3>
<p>그런데 log를 보니
첫 실행 후 <code>&quot;에러: DB 연결 실패&quot;</code> / <code>&quot;정상: 익명 로그인 성공&quot;</code> 이렇게 출력됐다.
두번째 실행부터는 <code>&quot;정상: DB 연결 성공&quot;</code> 이렇게 출력됐다.</p>
<p>이렇게 되는 이유는 잘 모르겠지만 재시작하면 잘 동작하는 듯 하니, TextView의 초기text에 안내문구를 넣었다. (<code>&quot;설치 후 첫 사용이시군요. 앱을 재시작해주시면 정상 작동됩니다.&quot;</code>)</p>
<h2 id="아직-못-한-부분">아직 못 한 부분</h2>
<p>앱 삭제 후 재설치 시 새로운 계정으로 로그인하게 되면서 이전 계정은 유령 계정이 되는데, 이렇게 안 쓰는 익명계정을 삭제하는 방법은 아직 공부 중이다.</p>
<p>원래 익명 로그인이 이렇게 내내 쓰는 게 아니라 잠깐 쓰라고 만든 기능인 것 같기도 하다. firebase에 만든 지 30일 된 익명 계정은 자동으로 삭제해주는 서비스(아마 유료?)가 있고, 공식 문서에도 익명 계정에서 제대로 회원가입한 계정으로 넘어가는 내용이 있는 걸 보면.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android+Firebase] Android 앱에서 데이터베이스 읽기]]></title>
            <link>https://velog.io/@2-pi-r/AndroidFirebase-Android-%EC%95%B1%EC%97%90%EC%84%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%9D%BD%EA%B8%B0</link>
            <guid>https://velog.io/@2-pi-r/AndroidFirebase-Android-%EC%95%B1%EC%97%90%EC%84%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%9D%BD%EA%B8%B0</guid>
            <pubDate>Mon, 18 Dec 2023 15:51:40 GMT</pubDate>
            <description><![CDATA[<p>나는 보안 규칙을 수정해서 쓰기 권한을 없앴으니까 읽기만 할 수 있다.</p>
<h1 id="참고자료-공식-문서">참고자료 (공식 문서)</h1>
<ul>
<li><a href="https://firebase.google.com/docs/database/android/start?hl=ko">Firebase에 앱 연결하기</a></li>
<li><a href="https://firebase.google.com/docs/database/android/read-and-write?hl=ko#java">Android에서 데이터 읽기 및 쓰기</a></li>
<li><a href="https://firebase.google.com/docs/database/android/lists-of-data?hl=ko">Android에서 데이터 목록 다루기</a></li>
</ul>
<h1 id="1-데이터베이스를-참조해서-데이터-스냅샷-받아오기">1. 데이터베이스를 참조해서 데이터 스냅샷 받아오기</h1>
<h2 id="기본-문법">기본 문법</h2>
<ul>
<li>onCreate<ul>
<li>main 같은 함수라고 이해했다. onCreate 밖에서 어떤 함수를 정의하면, 이 안에서도 꼭 한 번 실행해야 한다. 안 그러면 함수 정의만 해놓고 main에서 한 번도 호출 안 하는 거다.</li>
</ul>
</li>
<li>getReference<ul>
<li>인수의 기본값은 root(즉 &quot;/&quot;)이다. 만약 &quot;exKey&quot;를 넣으면 root 바로 아래의 &quot;exKey&quot;라는 key를 참조하게 된다.</li>
<li>해당 위치 + 하위 데이터를 모니터링한다고 생각하면 된다.</li>
</ul>
</li>
<li>addValueEventListener<ul>
<li>JAVA 문법에서 이벤트 리스너 추가하는 거 모르는 사람은 개념 한 번 훑어보고 오는 걸 추천한다.</li>
<li>실시간으로 DB와 소통하려면 이걸 쓰면 된다.</li>
<li>만약 그게 아니라 DB에서 한 번 읽어오고 마는 거라면 이거 대신 addListenerForSingleValueEvent 쓰면 된다. 다른 부분은 다 똑같다.</li>
<li>onDataChange<ul>
<li>Firebase Realtime Database는 깃처럼 데이터베이스의 스냅샷을 찍는데, 그걸 dataSnapshot이라고 한다.</li>
<li>처음에 한 번, 그 후로 데이터가 바뀔 때마다 한 번씩 호출된다.</li>
</ul>
</li>
<li>onCancelled<ul>
<li>이때 읽기 권한이 없으면 Cancel되면서 이 함수가 호출된다.</li>
</ul>
</li>
</ul>
</li>
<li>디버깅<ul>
<li>바로 이전 게시글에서 설명했듯, System.out.println 해서 프린트하면 Logcat에서 확인할 수 있다.
<img src="https://velog.velcdn.com/images/2-pi-r/post/cd193925-222a-48c4-b850-859676f3989e/image.png" alt=""></li>
</ul>
</li>
</ul>
<pre><code>package ~~;

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

import com.google.firebase.FirebaseApp;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;

public class MainActivity extends AppCompatActivity {
    private FirebaseDatabase mDatabase;
    private DatabaseReference mReference;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // 기본 설정 (activity_main 파일의 내용을 앱 화면에 보여줌.)
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 데이터베이스에서 값 읽어오기
        FirebaseApp.initializeApp(this);
        mDatabase = FirebaseDatabase.getInstance();
        mReference = mDatabase.getReference(); // root를 참조

        mReference.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                System.out.println(&quot;데이터 성공적으로 받아옴&quot;);
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                System.out.println(&quot;에러&quot;);
            }
        });
    }
}</code></pre><h2 id="error">ERROR</h2>
<p>제대로 썼는데! 저 코드에 문제가 없는데!! 데이터베이스를 아무리 수정해봐도 onDataChange 함수가 아예 호출되지 않는 문제가 있었다.</p>
<h3 id="원인">원인</h3>
<p>알고 보니 Logcat에 이런 게 찍혀있었고</p>
<blockquote>
<p>Firebase Database connection was forcefully killed by the server. Will not attempt reconnect. Reason: Database lives in a different region. Please change your database URL to <a href="https://miu404-whataretheydoingnow-default-rtdb.asia-southeast1.firebasedatabase.app">https://miu404-whataretheydoingnow-default-rtdb.asia-southeast1.firebasedatabase.app</a></p>
</blockquote>
<p>챗GPT한테 물어보니까 이렇다고 한다.</p>
<blockquote>
<p>이 경고는 Firebase Database의 연결이 서버에 의해 강제로 종료되었고, 재연결을 시도하지 않을 것이라고 알려주고 있습니다. 그 이유는 &quot;Database lives in a different region&quot;이라고 명시되어 있습니다. 이는 데이터베이스가 서버와 다른 지역에 위치해 있음을 나타냅니다.</p>
</blockquote>
<p>Firebase 프로젝트 만들 때 내가 위치를 싱가포르로 선택했는데, 이때 US(미국)을 선택한 게 아니면 이런 문제가 발생한다고 한다.</p>
<ul>
<li><p>출처 : <a href="https://velog.io/@chris_seed/Firebase-Database-%EC%84%A0%ED%83%9D-%EB%B0%8F-%EC%82%AC%EC%9A%A9#error">다른 분 블로그. 여기서 ERROR 부분. 이 분은 JAVA 말고 코틀린 쓰시는 것 같다.</a></p>
<h3 id="해결-방법">해결 방법</h3>
</li>
<li><p>검색 키워드 : <em>Database lives in a different region.</em></p>
</li>
<li><p>참고자료 : <a href="https://stackoverflow.com/questions/68806876/firebase-realtime-database-connection-killed-different-region">스택오버플로우</a></p>
<ul>
<li><p>방법을 2가지 알려줬는데, url을 직접 지정하는 방법은 자꾸 오류가 났다. 그래서 다른 방법을 썼더니 됐다.</p>
</li>
<li><p>간단하다. <strong>google-services.json 파일을 다시 다운받으면 된다.</strong></p>
</li>
</ul>
</li>
<li><p>자세한 과정은 아래와 같다.</p>
<ol>
<li>프로젝트 설정으로 이동한다.
<img src="https://velog.velcdn.com/images/2-pi-r/post/96dce7ae-360e-4d45-94ee-ca9f716a93b1/image.png" alt=""></li>
<li>스크롤을 내리고, google-services.json 파일을 다시 다운받는다.
<img src="https://velog.velcdn.com/images/2-pi-r/post/7cead593-22ed-4b44-87e1-2f3018ad9ccd/image.png" alt="">
<img src="https://velog.velcdn.com/images/2-pi-r/post/977d9dea-140f-46a0-b4ea-46d9315dc20c/image.png" alt=""></li>
<li>google-services.json 파일을 [프로젝트 폴더]-[app 폴더] 아래로 옮긴다.</li>
</ol>
</li>
</ul>
<p>끝.</p>
<h1 id="2-스냅샷으로부터-데이터를-읽어들이고-java-hashmap에-저장하기">2. 스냅샷으로부터 데이터를 읽어들이고, JAVA Hashmap에 저장하기</h1>
<h2 id="기본-문법-1">기본 문법</h2>
<h3 id="data가-string-string-쌍으로만-이루어져-있을-때">data가 (string, string) 쌍으로만 이루어져 있을 때</h3>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/fb5fabff-cf86-4b48-9759-20033e096415/image.png" alt=""></p>
<pre><code>import com.google.firebase.database.GenericTypeIndicator;
import java.util.HashMap;

// (중략)

GenericTypeIndicator&lt;HashMap&lt;String, Object&gt;&gt; genericTypeIndicator = new GenericTypeIndicator&lt;HashMap&lt;String, Object&gt;&gt;() {}; // GenericTypeIndicator를 사용하여 제네릭 유형을 지정

// onDataChange 함수 안에서
HashMap&lt;String, Object&gt; data = dataSnapshot.getValue(genericTypeIndicator); // GenericTypeIndicator를 사용하여 DataSnapshot에서 데이터를 가져오기</code></pre><p>이걸 <code>System.out.println(data);</code>로 print해보면 이렇게 된다.
<img src="https://velog.velcdn.com/images/2-pi-r/post/afb5b19b-84f3-490f-93ca-e57219c90b6d/image.png" alt=""></p>
<h3 id="data가-string-object-쌍일-때">data가 (string, object) 쌍일 때</h3>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/0ee2401a-ed68-4361-8255-1c8a60949cb2/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android+Firebase] Android Studio에서 기초 디버깅하기 (생초보)]]></title>
            <link>https://velog.io/@2-pi-r/AndroidFirebase-Android-Studio-%EB%94%94%EB%B2%84%EA%B9%85-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@2-pi-r/AndroidFirebase-Android-Studio-%EB%94%94%EB%B2%84%EA%B9%85-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Sun, 17 Dec 2023 14:30:46 GMT</pubDate>
            <description><![CDATA[<p>Android Studio를 어제 설치한 사람으로서, 다른 사람들은 좀 덜 헤매기를 바라며 적어본다.</p>
<h2 id="방법1-print-해본다">방법1. print 해본다.</h2>
<ol>
<li>MainActivity.java에서 원하는 문구를 print한다.</li>
<li>Logcat에서 출력 결과를 확인할 수 있다.<br><img src="https://velog.velcdn.com/images/2-pi-r/post/1c41487f-c7f4-42c8-8678-cc04f2055bff/image.png" alt=""></li>
</ol>
<h2 id="방법2-log를-찍어본다">방법2. Log를 찍어본다.</h2>
<ol>
<li><p>MainActivity.java에서 log를 찍는다.</p>
<ul>
<li>이때 Log를 import해야 아래 코드가 돌아간다.<pre><code>private static final String TAG = &quot;MainActivity&quot;;
</code></pre></li>
</ul>
<p>Log.d(TAG, &quot;Value is: &quot; + value);
```</p>
</li>
<li><p>Logcat에서 출력 결과를 확인할 수 있다.  </p>
</li>
</ol>
<h2 id="방법3-화면에-찍어본다">방법3. 화면에 찍어본다.</h2>
<ol>
<li>activity_main.xml에서 TextView를 추가한다.<ul>
<li>나는 프로젝트 만들 때 처음부터 TextView 있는 걸로 선택해서 이 과정이 필요 없었다.</li>
<li>프로젝트 만들 때 없는 걸로 선택했어도 괜찮다. 추가하면 된다.
1-1. Code를 클릭하면 이렇게 코드를 보여주고, Design을 선택하면 1-2 같은 화면을 보여준다.
<img src="https://velog.velcdn.com/images/2-pi-r/post/0c9991d4-41bf-49cb-a37d-c32bb3d53bee/image.png" alt="">
1-2. Design을 선택한 다음, 빨간 펜으로 표시해둔 TextView를 드래그해서 화면에 가져다 놓자. 그러면 알아서 코드에 TextView가 추가된다.
<img src="https://velog.velcdn.com/images/2-pi-r/post/a7d21e4f-daf0-46c5-aadd-6d0e946a1e79/image.png" alt=""></li>
</ul>
</li>
</ol>
<ol start="2">
<li><p>TextView에 id를 지정한다.</p>
<ul>
<li>아래는 id = myTextView로 지정하는 예시이다.
<img src="https://velog.velcdn.com/images/2-pi-r/post/199241bb-7a89-4a7d-8976-fc084f8c0d10/image.png" alt=""></li>
</ul>
</li>
<li><p>이제 MainActivity.java에서 아래와 같이 접근할 수 있다.</p>
<pre><code>import android.widget.TextView;

// (중략)

TextView mTextView = = findViewById(R.id.myTextView); // R.id.textView는 레이아웃 XML에서 정의된 TextView의 ID</code></pre></li>
<li><p>아래 코드로 mTextView 변수가 가리키는 TextView 블록의 text를 원하는 내용으로 set할 수 있다.</p>
<pre><code>mTextView.setText(&quot;내용&quot;);</code></pre></li>
<li><p>확인
5-1. [Run] - [Run &#39;App&#39;] 눌러서 앱을 실행한다. <img src="https://velog.velcdn.com/images/2-pi-r/post/6f040553-4a7e-4492-84ab-e4d431d2202e/image.png" alt="">
5-2. [Tools] - [Device Manager] 또는 [빨간 펜으로 표시한 버튼]을 눌러서 Device Manager를 켠다.
<img src="https://velog.velcdn.com/images/2-pi-r/post/aa61d54d-38a2-475d-842b-945e4d27eee1/image.png" alt="">
5-3. ▶버튼을 눌러서 시작한다. (나는 이미 실행 중이라 ■버튼이 보인다.)
<img src="https://velog.velcdn.com/images/2-pi-r/post/1872aa18-0d5d-40d3-bec2-a3a331d827d1/image.png" alt=""></p>
<ul>
<li>그러면 이렇게 보인다. 기존 문구인 &quot;Hello World!&quot;는 사라지고 &quot;내용&quot;으로 덮어쓰기된 것을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/2-pi-r/post/344986dd-825a-4793-9d40-4246d7e84744/image.png" alt=""></li>
</ul>
<p><img src="https://velog.velcdn.com/images/2-pi-r/post/9eaad25b-68d2-49e5-bceb-5ebc5ab8fa42/image.png" alt=""></p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android+Firebase] 데이터베이스에 데이터 넣기]]></title>
            <link>https://velog.io/@2-pi-r/AndroidFirebase-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%97%90-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%84%A3%EA%B8%B0</link>
            <guid>https://velog.io/@2-pi-r/AndroidFirebase-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%97%90-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%84%A3%EA%B8%B0</guid>
            <pubDate>Sun, 17 Dec 2023 12:51:19 GMT</pubDate>
            <description><![CDATA[<p>Firebase에서는 JSON 파일을 읽어들여서 데이터를 생성할 수 있다. 나는 엑셀로 데이터를 가지고 있기 때문에, 이걸 JSON 파일로 바꿔서 사용해야 한다.</p>
<h2 id="엑셀→json-바꾸기">엑셀→JSON 바꾸기</h2>
<h3 id="1-변환">1. 변환</h3>
<p>나는 <a href="https://shancarter.github.io/mr-data-converter/">변환 사이트</a>를 이용했다.</p>
<ul>
<li>변환 전 (엑셀)
<img src="https://velog.velcdn.com/images/2-pi-r/post/ffdc3bb1-4c55-453a-91f6-777e4ff9f0c5/image.png" alt=""></li>
<li>변환 후 (json)
<img src="https://velog.velcdn.com/images/2-pi-r/post/c660700e-b515-4560-850e-fa01a048413a/image.png" alt=""><ul>
<li>이 코드를 메모장에 복사 붙여넣기 해서 확장자를 .json으로 저장하면 된다.</li>
</ul>
</li>
<li>Firebase에서 그대로 사용하면 이런 에러가 뜬다.
<img src="https://velog.velcdn.com/images/2-pi-r/post/f58f8cc3-4f48-4558-81c5-0c3ec37819db/image.png" alt=""><ul>
<li>내가 엑셀에서 데이터 형식을 저런 식으로 작성해서 json으로 바꾸면 key가 없다. key는 비워둘 수 없으므로 에러가 뜨는 것이다.</li>
</ul>
</li>
</ul>
<h3 id="2-적절한-형태로-json-파일-수정">2. 적절한 형태로 JSON 파일 수정</h3>
<ul>
<li><p>참고자료 : <a href="https://firebase.google.com/docs/database/web/structure-data?hl=ko&amp;authuser=0&amp;_gl=1*1jcy9ve*_ga*MTI2MzgwNzI2NC4xNzAyNzk3NzYz*_ga_CW55HF8NVT*MTcwMjgxMTUxMC4zLjEuMTcwMjgxMTY3OC42MC4wLjA.#section-limitations">데이터베이스 구조화</a></p>
<ul>
<li>공식 문서. JSON 파일이 어떻게 생겨야 하는지를 알려준다.</li>
<li>대표 예시) <img src="https://velog.velcdn.com/images/2-pi-r/post/dca4e6ec-1860-4931-860c-8bfb115eca40/image.png" alt=""></li>
<li>key에 숫자 쓸 때 주의. 그런데 &quot;1&quot; 이런 건 문자열이라서 괜찮다.</li>
<li>key에 특수문자 쓸 수 없다. 비워놓을 수도 없다.</li>
<li>문자열 쓸 때 작은 따옴표로 쓰면 오류 난다. 큰 따옴표를 쓰자.</li>
</ul>
</li>
<li><p>수정사항
<img src="https://velog.velcdn.com/images/2-pi-r/post/34f37ab2-0298-420a-bbb1-ca465d1b2d99/image.png" alt=""></p>
<ul>
<li>[]를 {}로 바꾸고,</li>
<li>key를 비워놓을 수 없으니까 하나하나 붙여줘야 한다.<ul>
<li>하나하나 하기 귀찮으니까 파이썬 코드를 짜서 해결했다. 아래에 첨부한다. (다른 사람이 재사용하기 쉽게 약간 수정했다.)<pre><code>data = [] # [] 대신 1에서 변환한 코드 붙여넣기
with open(&#39;파일명.json&#39;, &#39;w&#39;, encoding=&quot;utf-8&quot;) as file:
file.write(&#39;{\n&#39;)
for cnt in range(len(data)):
  if cnt != 0:
      file.write(&#39;,\n&#39;)
  file.write(f&#39;\t&quot;{cnt}&quot;: &#39;)
  file.write(&#39;{\n&#39;)
  for key, value in data[cnt].items():
      if key != &quot;date&quot;: # date : 1열 이름
          file.write(&#39;,\n&#39;)
      file.write(f&#39;\t\t&quot;{key}&quot;: &quot;{value}&quot;&#39;)
  file.write(&#39;\n\t}&#39;)
file.write(&#39;\n}&#39;)</code></pre></li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="json-파일로-데이터-생성하기">JSON 파일로 데이터 생성하기</h2>
<ol>
<li><img src="https://velog.velcdn.com/images/2-pi-r/post/898dc1c4-fdf0-4fd0-b1c2-2c858f1f8f12/image.png" alt=""></li>
<li><img src="https://velog.velcdn.com/images/2-pi-r/post/15aca843-a9d0-4541-bbaa-ebc4b511b1fc/image.png" alt=""></li>
</ol>
<ul>
<li>(주의!) 데이터가 추가되는 것이 아니라 덮어쓰기된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android+Firebase] 데이터베이스 연결]]></title>
            <link>https://velog.io/@2-pi-r/AndroidFirebase-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EA%B2%B0</link>
            <guid>https://velog.io/@2-pi-r/AndroidFirebase-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EA%B2%B0</guid>
            <pubDate>Sun, 17 Dec 2023 09:51:29 GMT</pubDate>
            <description><![CDATA[<p>다음으로 뭐부터 할까 하다가 데이터베이스와 연결을 해보기로 했다.
android 프로젝트를 만들었다면, Firebase를 사용하기 위해 다음을 해야 한다.</p>
<h2 id="데이터베이스-선택">데이터베이스 선택</h2>
<ul>
<li>참고자료 : <a href="https://firebase.google.com/docs/database/rtdb-vs-firestore?hl=ko">데이터베이스 선택</a><ul>
<li>공식 문서.</li>
<li>Firebase는 Cloud Firestore, 실시간 데이터베이스 이렇게 2가지를 지원한다. 이 문서에서 몇 가지 질문에 대답하면 둘 중 어떤 것을 사용할지 추천해준다.</li>
<li>나는 실시간 데이터베이스를 사용하기로 했다.</li>
</ul>
</li>
</ul>
<h2 id="firebase와-android-앱-연결">Firebase와 Android 앱 연결</h2>
<h3 id="1-firebase-프로젝트를-만들고-firebase에-앱-등록하기">1. Firebase 프로젝트를 만들고, Firebase에 앱 등록하기</h3>
<ul>
<li>참고자료: <a href="https://firebase.google.com/docs/android/setup?hl=ko#analytics-enabled">Android 프로젝트에 Firebase 연결하는 방법</a><ul>
<li>공식 문서.</li>
<li>이 문서에서 시키는 대로 하면 금방 된다. 옵션1, 2가 있는데 1을 권장한대서 그걸로 했다.</li>
<li>아까 android-studio에서 프로젝트 생성할 때 분명 JAVA로 했는데, 저 문서에서 볼 때는 Kotiln을 봤다. 나도 이유는 잘 모르겠지만 android 프로젝트에 있는 파일명이 build.gradle이 아니라 build.gradle.kts니까 이렇게 하는 게 맞을 것이다. <img src="https://velog.velcdn.com/images/2-pi-r/post/ac0ae3d3-13e4-41f5-9e37-9486b6117ca6/image.png" alt=""></li>
<li>build.gradle.kts 파일은 이 위치에 있다. 파일이 2개인데 루트 수준(프로젝트 수준)과 모듈(앱 수준)을 구분해야 한다.
<img src="https://velog.velcdn.com/images/2-pi-r/post/34856081-15b3-49f4-8d19-31e3d55801e2/image.png" alt=""></li>
</ul>
</li>
</ul>
<h3 id="2-firebase에서-db-만들기">2. Firebase에서 DB 만들기.</h3>
<ul>
<li>참고자료 : <a href="https://firebase.google.com/docs/database/android/start?hl=ko">Firebase에 앱 연결하기</a><ul>
<li>공식 문서. 여기서 &#39;데이터베이스 만들기&#39; 항목만 참고했다.</li>
<li>테스트 모드, 잠금 모드를 선택해야 한다. 잘 모르겠지만... 이 문서의 &#39;웹, Apple 또는 Android SDK를 시작하려면 테스트 모드를 선택하세요.&#39;라는 문구 때문에 일단 테스트 모드로 시작했다.</li>
</ul>
</li>
</ul>
<h3 id="3-보안-규칙-수정하기">3. 보안 규칙 수정하기</h3>
<p>1, 2는 확실히 이 순서로 하는 게 맞는데 3부터는 잘 모르겠다.</p>
<ul>
<li>참고자료 : <a href="https://firebase.google.com/docs/database/security?hl=ko">Firebase 실시간 데이터베이스 보안 규칙 이해</a><ul>
<li>공식 문서.</li>
<li>다른 문서와는 다르게 그대로 따라하기만 되는 류는 아니고, 말 그대로 이해를 돕는다.</li>
</ul>
</li>
<li>나는 테스트 모드로 시작했는데, 이러면 누구나 자유롭게 읽고 쓸 수 있다. 아래는 현재 보안 규칙.
<img src="https://velog.velcdn.com/images/2-pi-r/post/da475ad0-0c4a-44f0-b260-088f8d2db95d/image.png" alt=""></li>
<li>내가 원하는 것 : 읽기는 누구나 할 수 있지만, 쓰기는 나만 할 수 있도록<ul>
<li>이래도 되는지는 잘 모르겠지만... 이렇게 수정해봤다. 일단은 Firebase console에서 직접 데이터 수정해봤는데 다행히 잘 된다.
<del><em>(당신이 보고 있는 이 글이 수정되지 않았다면 문제가 일어나지 않은 거겠죠...)</em></del> 
<img src="https://velog.velcdn.com/images/2-pi-r/post/65cce758-bb6b-47a0-ae45-8b4a360bbc25/image.png" alt=""></li>
<li>보안규칙 위험하다고 자꾸 메일이 와서 수정하기로 했다. 자세한 내용은 <a href="https://velog.io/@2-pi-r/AndroidFirebase-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%B3%B4%EC%95%88%EA%B7%9C%EC%B9%99">다른 글</a>에서.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android+Firebase] 프로젝트 생성]]></title>
            <link>https://velog.io/@2-pi-r/AndroidFirebase-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@2-pi-r/AndroidFirebase-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1</guid>
            <pubDate>Sun, 17 Dec 2023 08:36:35 GMT</pubDate>
            <description><![CDATA[<h2 id="방법">방법</h2>
<ul>
<li>참고자료 : <a href="https://www.opentutorials.org/course/3930/26656">생활코딩님 영상</a><ul>
<li>이전 글에서 참고자료로 적었던 영상. 보고 따라하면 금방 된다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android+Firebase] 입문용 프로젝트 구상]]></title>
            <link>https://velog.io/@2-pi-r/AndroidFirebase-%EC%9E%85%EB%AC%B8%EC%9A%A9-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%EC%83%81</link>
            <guid>https://velog.io/@2-pi-r/AndroidFirebase-%EC%9E%85%EB%AC%B8%EC%9A%A9-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%EC%83%81</guid>
            <pubDate>Sun, 17 Dec 2023 08:18:28 GMT</pubDate>
            <description><![CDATA[<p>웹 개발은 프론트, 백 둘 다 해봤는데 앱 개발은 처음이다. 마침 만들고 싶은 앱이 있어서, 이 기회에 앱 개발도 경험해보려고 한다.</p>
<h2 id="앱-구상">앱 구상</h2>
<h3 id="주제">주제</h3>
<p>어떤 드라마를 좋아하는데, 감사하게도 타임라인을 정리해주신 분이 계시다. 이걸 이용해서 특정 날짜가 되면 &quot;오늘 몇 시 몇 분에 드라마 상에서 이런 일이 일어났어!&quot;하고 알림을 띄워주는 앱을 만들고 싶다.</p>
<h3 id="주요-기능-20240103-수정">주요 기능 (2024.01.03 수정)</h3>
<ul>
<li>아침에 한 번 알림 띄우기<ul>
<li>오늘치 데이터 있으니까 앱 확인하세요~ 정도의 알림</li>
<li>타임라인 있는 날만 띄움</li>
</ul>
</li>
<li>메인 화면<ul>
<li>오늘의 타임라인 - 내용 (일본어, 한국어)</li>
<li>이전, 다음 타임라인 - 날짜만 (내용X)</li>
</ul>
</li>
<li><del>사이드 바</del><ul>
<li>원본 블로그 글 링크 → 맨 하단 버튼으로</li>
<li><del>개발자 정보</del></li>
<li><del>개발자에게 연락하기 (번역 수정 요청, 오류 문의 등)</del></li>
</ul>
</li>
</ul>
<h3 id="기타-기능-나중에-여유-되면-추가">기타 기능 (나중에 여유 되면 추가)</h3>
<ul>
<li>국가 설정 - 일본, 한국</li>
</ul>
<h2 id="개발-스택">개발 스택</h2>
<ul>
<li>통합 개발 환경 : android-studio</li>
<li>프로그래밍 언어 : JAVA</li>
<li>데이터베이스 : Firebase</li>
</ul>
<h3 id="선택-이유">선택 이유</h3>
<ul>
<li>android-studio<ul>
<li>안드로이드를 개발할 때 가장 많이 쓰인다고 한다.  </li>
</ul>
</li>
<li>JAVA<ul>
<li>android-studio는 JAVA와 Kotlin을 지원한다. Kotlin은 아예 모르고, JAVA는 기본적으로 다룰 줄 안다.</li>
<li>이 프로젝트의 목표는 간단한 앱을 빠르게 완성해서 배포해보는 것이기 때문에 이미 알고 있는 JAVA를 선택했다.</li>
</ul>
</li>
<li>Firebase<ul>
<li>웹 개발할 때 데이터베이스로 Firebase를 써본 적이 있다.</li>
<li>Firebase는 JSON 형식의 데이터를 사용한다. 내가 가지고 있는 데이터는 표 형태(엑셀)인데, 이걸 JSON으로 쉽게 변환할 수 있을 것 같다.</li>
</ul>
</li>
</ul>
<h2 id="참고자료">참고자료</h2>
<ul>
<li><a href="https://www.opentutorials.org/course/3930/26656">생활코딩님 영상</a> : 7분짜리 짧은 영상으로, 처음에 앱 개발에 대해 아무것도 모르는 상태에서 보기에 좋다. 무엇을 깔아서 어떻게 시작해야 하는지 알 수 있었다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>