<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>gourd_erased.log</title>
        <link>https://velog.io/</link>
        <description>앞길막막 전과생</description>
        <lastBuildDate>Tue, 13 Aug 2024 05:45:40 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>gourd_erased.log</title>
            <url>https://velog.velcdn.com/images/gourd_erased/profile/abab4a0f-490c-405c-8912-b7b506658508/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. gourd_erased.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/gourd_erased" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[IN-GPS] pyside6으로 GUI프로그램 만들기 - 구현 방식]]></title>
            <link>https://velog.io/@gourd_erased/IN-GPS-pyside6%EC%9C%BC%EB%A1%9C-GUI%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EA%B5%AC%ED%98%84-%EB%B0%A9%EC%8B%9D</link>
            <guid>https://velog.io/@gourd_erased/IN-GPS-pyside6%EC%9C%BC%EB%A1%9C-GUI%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EA%B5%AC%ED%98%84-%EB%B0%A9%EC%8B%9D</guid>
            <pubDate>Tue, 13 Aug 2024 05:45:40 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/gourd_erased/post/dc5c53a9-560b-4476-b09a-f69dd010c81b/image.png" alt=""></p>
<pre><code>출처 : Qt Wiki</code></pre><h3 id="ui-구현-방식-변경">UI 구현 방식 변경</h3>
<p><a href="https://velog.io/@gourd_erased/IN-GPS-with-%EC%A4%91%EB%B6%80%EB%B0%9C%EC%A0%84-React%EC%97%90%EC%84%9C-Python-%ED%95%A8%EC%88%98-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0">리액트로 파이썬 실행하기</a>
리액트를 쓰기 위해서는 이전 글에서 언급했듯이 많은 비효율이 있었다.
당연히 이전 글과 같은 방식으로는 절대!! 개발을 해서는 안된다.</p>
<p>결국 python으로 ui를 만들기로 했다.</p>
<hr>
<h3 id="pyqt와-pyside">PyQt와 PySide</h3>
<p>파이썬 GUI 프로그래밍 프레임워크에는 PyQt를 다들 많이 활용하는 것 같았다.
PyQt는 Qt를 파이썬에서 활용 가능하도록 한 것이다. 
이전까지는 pyqt자체가 하나의 프레임워크인 줄 알았었다.</p>
<p>그럼 또 qt란 무엇일까
qt란 C++로 돌아가는 크로스플랫폼프레임워크다.
<code>QML</code>(qt Modeling Language)라는 자체 언어도 가지고 있다.</p>
<p><code>PyQt</code> 는 qt로 만든 레이아웃에 파이썬 코드를 연결하여 GUI를 제작할 수 있게 해주는 프레임워크이다.
다만 <code>PyQt</code>는 <strong>GPL라이선스</strong>를 가지고 있다. 상업적 배포 프로그램은 모든 소스코드를 공개할 의무가 있다. 
반면 <code>PySide</code>의 경우 <code>PyQt</code>와 거의 동일하지만 <strong>LGPL</strong> 라이선스로 의무공개는 없다.
따라서 PySide로 결정을 했다.</p>
<hr>
<h3 id="pyside-개발-방식">PySide 개발 방식</h3>
<p>알아봤을 때는 크게 세가지 방식이 있었다.</p>
<p><strong>1. 코드 기반 방식</strong>
<strong>2.  QML 기반 방식</strong>
<strong>3. 디자이너 기반 방식</strong></p>
<p>여기서 나는 모든 방식으로 간단히 화면을 구현해봤다.</p>
<h4 id="1-파이썬-코드-기반-방식">1. 파이썬 코드 기반 방식</h4>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/68125f76-37a4-406b-8f8f-cd2db95d760c/image.png" alt=""></p>
<ul>
<li><strong>장점</strong><ul>
<li>UI코드가 같이 있기에 코드의 흐름을 한눈에 보기에 편하다.</li>
</ul>
</li>
<li><strong>단점</strong><ul>
<li>다른 앱 플랫폼(ios, aos)처럼 시뮬을 보면서 개발이 안된다.</li>
<li>따라서 컴파일을 하면서 확인해야하므로 화면 배치의 어려움을 겪었다.</li>
<li>또한 커스텀하기 위한 css코드도 같이 넣어야 하기에 코드의 길이가 길어진다.</li>
</ul>
</li>
</ul>
<hr>
<h4 id="2qml-기반-방식">2.QML 기반 방식</h4>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/d62ebf3d-8ced-4edc-9b29-062d80314372/image.png" alt=""></p>
<ul>
<li><strong>장점</strong><ul>
<li>qml(qt modeling language)은 ui설계용 선언형 언어이며  javascript와도 연동이 가능하다.</li>
<li>코드를 보면 알 수 있듯이 한눈에 알아보기 되게 편하도록 구현이 가능하다.</li>
</ul>
</li>
<li><strong>단점</strong><ul>
<li>새로운 언어를 배워야하기에 학습곡선이 필요하다.</li>
<li>python과의 상호작용이 복잡하다.</li>
</ul>
</li>
</ul>
<hr>
<h4 id="3디자이너-기반-방식">3.디자이너 기반 방식</h4>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/21c70256-ebca-410c-9dfc-4bb04f3bff54/image.png" alt=""></p>
<ul>
<li><strong>장점</strong><ul>
<li>시작적으로 ui구성이 가능해 빠른 개발이 가능하다.</li>
</ul>
</li>
<li><strong>단점</strong><ul>
<li>디자이너에서 만든 화면을 .ui로 저장후에 파이썬 코드로 변환을 해야한다.</li>
</ul>
</li>
</ul>
<hr>
<p>나는 아래의 이유들로 <code>qt디자이너 방식</code>을 선택했다.</p>
<p>우선 안드 개발과 익숙해져 있어 코드 변경이 화면에 바로 변경이 안되는 것이 너무 불편했다.
그리고 pyqt &amp; pyside 유튜브 레퍼런스의 거의 90%? 정도는 디자이너를 기반으로 진행한다.</p>
<p>사실 qml방식이 선언형 UI라 마음에 들었었지만 다음 이유가 너무 걸렸다.
현재 프로젝트는 산업경영공학과 연구실에서 진행하고 있다. 나는 용병느낌으로 잠시 동안 들어간 것이다. 이후에는 다른 팀원중 누군가가 수정을 해야할 수도 있다.</p>
<p>따라서 추후 큰 학습곡선 없이 수정하기에 용이한 디자이너 방식을 선택했다.</p>
<p>단점인 .ui 에서 파이썬 코드로의 변환은 플러그인이 해결을 해준다.
아래 Baram 개발자분의 영상으로 도움을 얻었다.</p>
<p><a href="https://youtu.be/BGJ5ZBXMkfY?si=vW66EACYeVw-7J3X">Baram 유튜브 - Vscode &amp; qt desinger 설정</a></p>
<p>Vscode를 통해 .ui파일을 생성 후 편집, 저장을 하면 바로 .ui에서 파이썬 코드로 변환을 해준다. 따라서 큰 불편함 없이 개발이 가능하다.</p>
<p>또한 이제는 기존 알고리즘 코드를 그대로 갖다 사용하면 된다.</p>
<hr>
<h3 id="앞으로">앞으로</h3>
<p>리액트를 공부한 것이 아깝기는 하지만 또 항상 앱/웹 개발만 하다가 pyside를 공부해보는 좋은 기회를 가지게 되었으니 괜찮다.
pyside도 생각보다는 레퍼런스가 적어 나중을 위해 기록을 잘해가며 개발을 하려한다.</p>
<p>매번 사이드 느낌의 프로젝트를 하다가 외주를 맡게 되니 더욱 책임감을 갖고 해야겠다...!..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[클밋] 스프링 애플리케이션에서 SSL 인증받으며 생긴 문제 - 해결]]></title>
            <link>https://velog.io/@gourd_erased/%ED%81%B4%EB%B0%8B-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90%EC%84%9C-SSL-%EC%9D%B8%EC%A6%9D%EB%B0%9B%EC%9C%BC%EB%A9%B0-%EC%83%9D%EA%B8%B4-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@gourd_erased/%ED%81%B4%EB%B0%8B-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90%EC%84%9C-SSL-%EC%9D%B8%EC%A6%9D%EB%B0%9B%EC%9C%BC%EB%A9%B0-%EC%83%9D%EA%B8%B4-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Tue, 16 Apr 2024 15:05:32 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>클라이밍 앱 &#39;클밋&#39;을 제작하며 고민/개발 내용을 기록한 글입니다.</p>
</blockquote>
<p><a href="https://velog.io/@gourd_erased/%ED%81%B4%EB%B0%8B-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90%EC%84%9C-SSL-%EC%9D%B8%EC%A6%9D%EB%B0%9B%EA%B8%B0">원인</a>
<a href="https://velog.io/@gourd_erased/%ED%81%B4%EB%B0%8B-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90%EC%84%9C-SSL-%EC%9D%B8%EC%A6%9D%EB%B0%9B%EC%9C%BC%EB%A9%B0-%EC%83%9D%EA%B8%B4-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0%EA%B3%BC%EC%A0%95">HTTP와 HTTPS</a></p>
<hr>
<h3 id="해결방식">해결방식</h3>
<p>2가지의 선택지가 있다.</p>
<p>ssl설정을 지금처럼 톰캣에서 해주는대신 인증서를 업데이트 하는 방식과 nginx에서 해주는 방식이 있다.</p>
<h4 id="ssl-설정-내장-톰캣-유지">ssl 설정 내장 톰캣 유지</h4>
<p>먼저 내장 톰캣 그대로 사용하는 방법이 있다.
만약 톰캣을 쓴다면 스프링 빌드를 어떻게 할 지 결정해야한다.</p>
<p>왜냐하면 톰캣의 ssl인증서는 런타임에 수정이 불가하다.
빌드시에 메모리에 올라가기 때문이다.
때문에 인증서가 갱신이 되고 이를 톰캣에서도 갖고 있게 하려면 빌드를 다시 해줘야한다.</p>
<p>현재 스프링 빌드를 깃허브 액션에서 하기 때문에 ec2에서 갱신되는 인증서를 다시 깃허브 액션 리눅스로 가져온 후에 스프링 빌드를 다시 해줘야한다.
이후 다시 도커 이미지 빌드 후 푸시,,,등 기존 배포 과정을 인증서 갱신때문에 다시 한번 해야한다.</p>
<blockquote>
<p><a href="https://velog.io/@gourd_erased/%ED%81%B4%EB%B0%8B-%EC%8A%A4%ED%94%84%EB%A7%81-%ED%94%84%EB%A1%9C%ED%8C%8C%EC%9D%BC-%EC%84%A4%EC%A0%95%EC%9D%84-%ED%86%B5%ED%95%9C-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EB%B6%84%EB%A6%AC%EB%8F%84%EC%BB%A4%EC%99%80-%EA%B9%83%ED%97%88%EB%B8%8C-%EC%95%A1%EC%85%98">배포 과정</a></p>
</blockquote>
<p>만약 이렇게 한다면 깃허브 액션 워크플로우를 스케줄로 crontab설정을 해 3개월에 한번씩 실행되게 해야한다.
뿐만 아니라 pem키가 갱신되는 것이기 때문에 인증서 갱신 후 p12변환도 매번 해줘야한다. 이 또한 쉘 스크립트로 작성 후 인증서를 갱신하는 crontab에 추가시켜줘야한다.</p>
<h4 id="nginx">nginx</h4>
<p>nginx에서는 ssl설정을 간편히 해줄 수 있다. 또한 인증서가 갱신되더라도 다시 빌드할 필요없이 ec2에서 nginx만 재시작해주면 되기 때문에 앞선 방법보다 훨씬 간편하다.</p>
<p>또한 포트 포워딩까지 같이 해줄 수 있기 때문에 tomcat에서 nginx로 ssl설정을 변경했다.</p>
<p>앞선 실수를 다시하지 않기 위해 단순히 따라하지 않고 코드마다 어떤 역할을 하는지 이해하며 작성했다.</p>
<p>아래 세 가지를 작성해줄 것이다.</p>
<ul>
<li>ssl 인증서 설정</li>
<li>80포트로 요청시 443포트로 포워딩</li>
<li>443포트 요청시 8080포트로 포워딩</li>
</ul>
<hr>
<h3 id="nginx-설정">nginx 설정</h3>
<p>Nginx는 구성 파일을 지정된 지시어를 통해 제어할 수 있다.
지시어는 <code>간단한 지시어</code>와 <code>블록 지시어</code>로 나뉜다.
전자는 세미콜론으로 후자는 {} 중괄호에 있는 지시어들을 포함한다.
만약 <code>블록 지시어</code> 내에 다른 지시어들이 포함된다면 <code>컨텍스트</code> (http, server, location)라고 한다.</p>
<p>디렉토리 구조도 알아보자</p>
<p>알아야할 파일은 크게 3가지가 있다.</p>
<ul>
<li>** nginx.conf**
  메인 설정 파일이다.
 연결 처리 방식, 타임아웃, 보안 설정 등등이 가능하다.</li>
<li>** sites-available **
  서버에서 가능한 모든 웹사이트의 구성 파일을 저장한다. 여기에 있는 파일은 Nginx에 의해 직접 참조되지 않는다. 
  활성화를 위해서는 <code>sites-enable</code> 디렉토리에 링크를 걸어야 한다.</li>
<li>** sites-enabled**
  <code>sites-available</code> 에서 만든 설정을 <code>sites-enabled</code> 에서 심볼릭 링크 형태로 활성화한다.</li>
</ul>
<pre><code>available과 enabled를 나누는 이유는 여러 도메인을 관리하거나 
복잡한 환경을 관리할 때 사용한다고 한다.
(출처 : 지피티)
available에 작성은 모두 해두고 
enabled에서 상황에 따라 활성화/비활성화가 가능하기 때문인것같다.</code></pre><p>따라서 available에서 설정을 해주면 된다.</p>
<p>만약 nginx.conf에서 수정한다면 아래 우선순위를 조심하자.</p>
<ol>
<li>server_name이 구체적으로 일치하는 경우
(와일드 카드보다 우선순위라는  뜻)</li>
<li>설정 파일의 로드 순서
server_name이 같을 경우 nginx.conf보다 sites-enabled에서 로드된 서버 블록이 우선 순위가 높다.</li>
</ol>
<pre><code class="language-shell">#etc/nginx/sites-available/
#sites-available에 있는 default파일은 주석처리

# 아래 블록을 http블록안에 추가해주면 된다.

# 80포트
server {
        listen 80;
        server_name www.testclimeet.shop;
        # 80포트를 https, 즉 443포트로 포워딩한다.
        # 301은 리소스가 이동한다는 http상태코드이다.
        # rquest_uri는 http로 요청했을때의 도메인 뒤 쿼리 스트링을 그대로 https로 전달해주는 것이다.
        return 301 https://testclimeet.shop$request_uri;
}

# 443 포트
server {
        listen 443 ssl;
        server_name www.testclimeet.shop;

        #ssl 인증서
        ssl_certificate /etc/letsencrypt/live/www.testclimeet.shop/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/www.testclimeet.shop/privkey.pem;

        location / {
                #스프링 포트로 포워딩
                proxy_pass http://localhost:8080;
        }
}
</code></pre>
<p>이후 <code>enabled</code>에 심볼릭 링크를 걸어주자</p>
<pre><code class="language-shell">sudo ln -s 
/etc/nginx/sites-available/www.testclimeet.shop 
/etc/nginx/sites-enabled/www.testclimeet.shop

# 만약 해제하고 싶다면
sudo unlink /etc/nginx/sites-enabled/www.testclimeet.shop
심볼릭 링크 자체만 제거하는 것이기 때문에 원본 파일은 남아있다.</code></pre>
<p>만약 스프링에서 ssl설정을 했줬다면 yml에서 ssl관련 코드를 지워야한다.</p>
<pre><code class="language-shell"># nginx설정파일 이상 확인
sudo nginx -t
sudo systemctl reload nginx</code></pre>
<p>nginx설정에 이상이 없는지 확인후 nginx 재시작까지 해준다면 아래와 같이 성공이다.</p>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/e23e959f-8aa8-49c2-8dfe-667f2b7a15e2/image.gif" alt=""></p>
<p><code>http://www.testclimeet.shop</code> 이 443으로 포워딩 한번,8080으로 포워딩 된다.
gif에서 스웨거로 한번 더 바뀌는 이유는 yml에서 아래 설정을 해주었기 때문이다.</p>
<pre><code class="language-yml">springdoc:
  swagger-ui:
    path: /</code></pre>
<hr>
<p>이제 3개월에 한번씩 갱신과 nginx재시작을 하도록 crontab을 작성해주면 인증서와 관련해 자동화는 끝나게 된다.
아직 클밋은 개발 과정에 있기에 crontab 설정은 배포 이후에 추가로 해주면 될 것 같다!</p>
<p>스프링이나 자바와 달리 개발 환경 설정 관련해서는 감자마냥 이해없이 따라만했다.
조금 더 시간이 걸리더라도 공식문서를 참고하며 한 줄 한 줄 이해에 기반한 코드를 작성해야함을 느꼈다.</p>
<blockquote>
<p>참고: <a href="https://nginx.org/en/docs/beginners_guide.html">nginx.org - begginers_guide</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[클밋] 스프링 애플리케이션에서 SSL 인증받으며 생긴 문제 - HTTP와  HTTPS]]></title>
            <link>https://velog.io/@gourd_erased/%ED%81%B4%EB%B0%8B-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90%EC%84%9C-SSL-%EC%9D%B8%EC%A6%9D%EB%B0%9B%EC%9C%BC%EB%A9%B0-%EC%83%9D%EA%B8%B4-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@gourd_erased/%ED%81%B4%EB%B0%8B-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90%EC%84%9C-SSL-%EC%9D%B8%EC%A6%9D%EB%B0%9B%EC%9C%BC%EB%A9%B0-%EC%83%9D%EA%B8%B4-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Sun, 14 Apr 2024 09:00:58 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>클라이밍 앱 &#39;클밋&#39;을 제작하며 고민/개발 내용을 기록한 글입니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/e28a9005-124a-4d54-9720-b4f878eb322f/image.png" alt=""></p>
<h3 id="해결-이전에-개념부터">해결 이전에 개념부터!</h3>
<p>당장 key파일을 옮겨서 해결하는 것보다 다시는 비슷한 일을 겪지 않게 위해 통신과정부터 개념을 이해하고자한다.
이전처럼 따라하지만 말고 왜 HTTPS를 써야하는지부터 이해해보자!</p>
<h4 id="http와-http">HTTP와 HTTP</h4>
<p>HTTP통신은 OSI7계층중 애플리케이션 계층에서 작동하는 프로토콜이다.</p>
<p>HTTP는 평문으로 통신을 해 중간자가 가로채면 그대로 읽을 수 있다. 
와이어샤크를 통해 패킷을 확인해보자!</p>
<p>이전에 진행했던 프로젝트 중에https를 적용하지 못했던 좋은 예시가 있다.
<a href="https://velog.io/@gourd_erased/Spring-Boot-JPA-%EC%87%BC%ED%95%91%EB%AA%B0-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%99%84%EC%84%B1-%EC%A0%9C%EC%B6%9C">쇼핑몰 프로젝트 기록</a></p>
<hr>
<p>회원가입을 했다. 프론트에서 서버로 아이디와 패스워드 이메일이 담긴 패킷이 날라갈 것이다.
비밀번호를 가로챌 수 있는지 확인해보자</p>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/fac137c6-406c-4894-b10b-d18d6b52d459/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/0a353aaf-d768-4930-9aee-c44208e17587/image.png" alt=""></p>
<p>와이어샤크 상단에 서버 ip로 필터링을 했다.
3-way handshake 이후 http통신한 것을 볼 수 있다.
이제 패킷을 자세하게 보면</p>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/430a57e3-acf6-4273-bb78-ac221b13e2de/image.png" alt=""></p>
<p>와이어샤크를 사용법을 블로그에서 잠깐 본 나도 손쉽게 중요정보를 확인할 수 있었다.</p>
<p>이번에는 HTTPS를 확인해보자. HTTPS가 되어있는 클밋 스테이징 서버의 관리자 회원가입을 진행해봤다.</p>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/0f8561b0-608d-4b60-8d84-bcd2acb9130f/image.png" alt=""></p>
<p>공격자는 난수밖에 볼 수 없다.</p>
<p><code>http</code> 라고 감청이 쉽게 되는 것은 아니지만 <code>HTTPS</code> 에 비해서는 상대적으로 취약하다.
보안이 좋은데다가 구글의 검색엔진 최적화까지 챙기기 위해서 <code>HTTPS</code>는 필수라고 봐야한다.</p>
<pre><code>HTTP 통신은 공공와이파이, 악성 소프트웨어, 
로컬 네트워크 등으로 감청이 가능하다고한다. [출처 : 지피티]</code></pre><blockquote>
<p>참고 : <a href="https://www.cloudflare.com/ko-kr/learning/ssl/why-is-http-not-secure/">cloudflare - HTTP와 HTTPS의 비교</a></p>
</blockquote>
<hr>
<h3 id="그렇다면-https는-어떻게-암호화할까">그렇다면 HTTPS는 어떻게 암호화할까?</h3>
<p>SSL이라는 하나의 통신 레이어를 추가해서 암호화를 한다. HTTPS는 SSL레이어 위에서 HTTP를 통과시키는 방식이라고 한다.
아까 본 평문의 데이터가 SSL레이어를 통과하면서 암호화되고 다시 목적지의 SSL레이어를 통과하면서 복호화되는 것이다.</p>
<p>그럼 아무나 복호화를 할 수 있나?
그건 또 아니다.</p>
<p>이를 알기 위해서는 비대칭키의 개념이 중요하다.</p>
<h4 id="비대칭키공개키">비대칭키(공개키)</h4>
<p>간단히 말해 한 쌍의 서로 다른 2개의 키가 있다고 생각하자.
A키로 암호화를 하면 무조건 B키로만 복호화를 할 수 있다.(대칭키)</p>
<pre><code>그럼 네이버는 B키를 가지고 있고 A키를 공개한다. 그러면
1. 유저들은 공개된 A키를 통해 암호화한다.
2. 네이버는 서버에서만 갖고 있는 B키를 통해 복호화한다.</code></pre><p>이렇게만 되면 문제가 없지만 한가지 문제가 있다.</p>
<p>다른 악의를 가진 제3자가 가짜 네이버 서버를 만들어 유저들에게 공개키 A&#39;를 뿌린 것이다.
그럼 유저들은 네이버에 로그인하기 위해 정보들을 정성스레 A&#39;키로 암호화한 후에 다른 목적지로 보내게 된다. 이를 제3자는 악용할 수 있다.</p>
<p>따라서 이를 막기 위해 암호화, 복호화에 다른 키를 쓰는 것이 비대칭키이다.</p>
<h4 id="하이브리드-방식">하이브리드 방식</h4>
<p>하지만 비대칭키가 장점만 있는 것은 아니다.
암호화, 복호화에 대칭키에 비해 시간이 걸린다는 단점이 있다.</p>
<p>따라서 HTTPS 통신은 키 방식 두가지를 섞어서 쓰고 있다.</p>
<p>간단히 말하면 핸드쉐이킹시 <code>비대칭키</code> 를 사용하고 이후 세션 유지 기간동안 통신에는 <code>대칭키</code>를 사용하는 것이다.</p>
<p>자세히 과정을 살펴보자.</p>
<ol>
<li>클라이언트는 사이트에 접속해 연결을 요청한다.</li>
<li>서버는 공개키와 비밀키중에서 공개키를 담은 인증서를 함께 보내준다.</li>
<li>브라우저를 통해 세션키가 받은 공개키를 통해 암호화가 된다.</li>
<li>세션키를 다시 서버로 보낸다.</li>
<li>서버에서는 갖고있는 비밀키로 복호화한다.</li>
<li>양쪽 다 세션키를 갖게 된다.</li>
</ol>
<h4 id="ca">CA</h4>
<p>사실 2번에서 한가지 과정을 더 거쳐야한다.
브라우저단에서 공개키가 네이버것인지 확인해야하는데 이를 인증해주는 기관이 Certificate Authority, CA이다. </p>
<p>네이버는 CA에 <code>SSL 인증서</code> 를 요청하여 인증서를 발급받는다. 그러면 유저들은 CA기관에서 미리 받아 브라우저(크롬, 사파리)에 저장된 인증서 리스트와 비교한다.
만약 있다면 인증서 안에 공개키를 얻을 수 있다.</p>
<p>이를 그림으로 정리해봤다.</p>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/1ef6405b-c02c-444b-9d51-3a880c1ef32d/image.png" alt=""></p>
<p>이제 각자의 세션키로 데이터를 암호화해서 대칭키 통신을 하게 된다.
때문에 앞서 와이어샤크로 봤을 때 암호화가 되어 보인 것이다.</p>
<hr>
<p>이제 이론에 대해 살펴봤으니 다음 포스팅으로는 클밋에 적용한 방식과 어떤 문제점이 있는지, 어떻게 해결할 지에 대해 작성할 것이다.</p>
<p><a href="https://velog.io/@gourd_erased/%ED%81%B4%EB%B0%8B-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90%EC%84%9C-SSL-%EC%9D%B8%EC%A6%9D%EB%B0%9B%EC%9C%BC%EB%A9%B0-%EC%83%9D%EA%B8%B4-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0">해결</a></p>
<blockquote>
<p>참고 : <a href="https://velog.io/@gkqkehs7/HTTP%EC%99%80-HTTPS-%EA%B7%B8%EB%A6%AC%EA%B3%A0-SSL">재능없는 개발자 - HTTP와 HTTPS 그리고 SSL</a>
<a href="https://100100e.tistory.com/317">100100e - Https는 공개키?</a>
<a href="https://tiptopsecurity.com/how-does-https-work-rsa-encryption-explained/">tiptopsecurity - How Does HTTPS Work?</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[클밋] 스프링 애플리케이션에서 SSL 인증받으며 생긴 문제 - 발급과 원인]]></title>
            <link>https://velog.io/@gourd_erased/%ED%81%B4%EB%B0%8B-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90%EC%84%9C-SSL-%EC%9D%B8%EC%A6%9D%EB%B0%9B%EA%B8%B0</link>
            <guid>https://velog.io/@gourd_erased/%ED%81%B4%EB%B0%8B-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90%EC%84%9C-SSL-%EC%9D%B8%EC%A6%9D%EB%B0%9B%EA%B8%B0</guid>
            <pubDate>Wed, 10 Apr 2024 13:17:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>클라이밍 앱 &#39;클밋&#39;을 제작하며 고민/개발 내용을 기록한 글입니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/850b0b76-bfec-44a8-9b30-a8f259ec467e/image.png" alt=""></p>
<blockquote>
<p>출처 : <a href="https://gvsc.rajephon.dev/">게비스콘 생성기</a></p>
</blockquote>
<h3 id="3개월-전">3개월 전,,,</h3>
<p>3개월 전, 클밋 프로젝트를 진행하며 2개의 도메인에 대한 인증서를 발급받았다. </p>
<p>이 글을 작성하는 이유는,  HTTPS 통신을 위해 발급받은 인증서와 관련해 갑자기 문제가 생겼기 때문이다. 당시 보안, 인증 과정에 대한 이해없이 단순히 블로그를 보며 따라만했고 이것이 얼마나 좋지 않은지 느꼈다.
개념부터 이해하는 과정을 정리하며 문제를 해결해보려한다.</p>
<hr>
<h3 id="ssl-인증서-발급">SSL 인증서 발급</h3>
<p>도메인은 가비아에서 구매했다.
<img src="https://velog.velcdn.com/images/gourd_erased/post/2d09102d-ce3c-4c85-a0b5-354ebc166ef8/image.png" alt=""></p>
<p>작성하며 생각해보니 그때 굳이 도메인을 왜 2개 샀을까란 생각도 문득 들었다. 다음에는 서브 도메인을 통해 하나의 도메인에 2개의 ip를 연결하는 방식을 고민해봐야겠다.</p>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/6ce351f5-c780-42ab-808a-7b74daf7737c/image.png" alt=""></p>
<p>A타입에 호스트 WWW로 ip연결 후에 잘 연결됐는지 확인했다.</p>
<p>이후 ec2로 접속해 certbot을 설치했다.</p>
<p>설치 후 나는 발급 방식 4가지 중에 standalone으로 80포트를 통해 발급하는 방식을 <del>선택했다.</del>
사실 선택이 아니라 블로그에서 하는 대로 따라했다.
<img src="https://velog.velcdn.com/images/gourd_erased/post/3b1d8a39-72d7-4d82-8d8d-2a28f407c235/image.png" alt=""></p>
<p><a href="https://eff-certbot.readthedocs.io/en/latest/using.html#certbot-commands">certbot 공식문서</a></p>
<blockquote>
<p>standalone방식을 사용했는데 이미 웹서버로 nginx를 쓰고 있었기에 nginx를 중지하며 발급받았었다. 자동으로 nginx설정을 해주는 옵션이 있었는데
공식문서를 한번이라도 읽어봤다면.....ㅎ</p>
</blockquote>
<p>아무튼 발급을 진행하면 
<img src="https://velog.velcdn.com/images/gourd_erased/post/890671d4-2501-4aca-9472-a0f46dc6dfa4/image.png" alt=""></p>
<p>/etc/letsencrypt/live/도메인 경로에 fullchain.pem 과 privkey.prem이 발급이 되었다고 나온다.</p>
<hr>
<h3 id="키-변환">키 변환</h3>
<p>이제 스프링에서 사용을 해야하기에 pem.key를 .p12로 바꿔야한다.
(Java 기반 애플리케이션에서 지원하는 KeyStore Type은 JKS, JCEKS, <strong>PKCS12(p12)</strong>가 있다.)</p>
<pre><code>sudo openssl pkcs12 
-export 
-in fullchain.pem 
-inkey privkey.pem 
-out keystore.p12 
-name ttp -CAfile chain.pem -caname root</code></pre><p>명령어를 입력후 비밀번호를 설정하면 keystore.p12파일이 생성된다.
이때 비밀번호를 잘 저장해두자</p>
<h4 id="스프링으로-가져오기">스프링으로 가져오기</h4>
<p>이제 이 인증서를 스프링 프로젝트의 resource폴더에 위치시켜야 https통신이 가능하다.
가져오기 위해 scp명령어를 사용하자.
하지만! 
아까 key파일 경로에 있던 live에 있는 파일을 그대로 가져오는 것에 계속 실패했다. 그래서 방식을 <code>로컬 -&gt; ec2 live디렉토리</code>에서 수정했다.
<code>ec2 live디렉토리 -&gt; ec2 home디렉토리</code> 로 빼낸 후에
<code>로컬 -&gt; ec2 home디렉토리</code>로 가져왔다. 중간에 home으로 빼내는 과정만 추가된 것이다.</p>
<pre><code class="language-shell">
#0. 권한 변경
sudo su로 해야 live 디렉토리 접근 가능

#1. 끄집어내
sudo cp /etc/letsencrypt/live/도메인 이름/keystore.p12 /home/ubuntu/
#2. 파일 권한을 현재 ubuntu사용자로

#3. mac터미널에서 복사
scp -i &quot;/Users/gourderased/Folders/keys/pem키 이름&quot; ubuntu@(ip수정):/home/ubuntu/keystore.p12 /Users/gourderased/Downloads/
</code></pre>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/f7c2d967-0cb1-4f42-92cf-2855b896c17d/image.png" alt=""></p>
<hr>
<h3 id="스프링-설정">스프링 설정</h3>
<p>이제 이 인증서를 resource에 들어가도록 하면 된다.
클밋의 경우 프라이빗 서브모듈에 넣어주었다.</p>
<p>빌드시에 서브모듈 디렉토리에서 resource로 복사되도록해 인증서가 외부에 노출되지 않도록 했다.
인증서가 노출된 경우 공격자가 암호화된 통신을 복호화할 수 있다.</p>
<p>이제 스프링의 yml에 ssl 관련 설정값을 넣어주면 된다.</p>
<pre><code class="language-yml">server:
  ssl:
      //classpath -&gt; default값은 resource라고 한다.
    //resource에 p12파일을 둘거면 그대로 사용하면 된다.
    key-store: classpath:keystore.p12
    key-store-password: 변환시 설정한 비밀번호
    key-store-type: PKCS12</code></pre>
<hr>
<h3 id="자동-갱신-설정">자동 갱신 설정</h3>
<p>이후 리눅스 스케줄러인 crontab을 통해 자동갱신설정을 해주면 된다.</p>
<pre><code class="language-bash"># 편집기 열기
crontab -e

    매일 새벽 3시 renew를 수행해 certbot이 만료된 인증서가 있는지 확인 &amp; 갱신
0 3 * * * /usr/bin/certbot renew --quiet

#quiet은 갱신시 별다른 로그를 남기지 않는 옵션이다.
#renew를 매일하는 것이 아닌 만료일이 임박했을 때만 수행되기 때문에 부담되지 않는다고한다.(출처 : 지피티)
</code></pre>
<p>그럼 이렇게 갱신해줘야하는 인증서는 만료 기한이 언제일까?</p>
<p>바로 <strong>3개월</strong>이다...!!
위 설정을 한 뒤 3개월 뒤인 지금 나는 인증서 만료로 서버에 api요청이 막힌 문제를 겪고 있다.</p>
<hr>
<h3 id="갱신도-해주었는데-왜">갱신도 해주었는데 왜???</h3>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/cc239404-41e0-49cc-8c03-e6312e0aa575/image.png" alt=""></p>
<p>처음에 인증서 만료됐다는 것을 봤을 때 의아했다. 갱신을 해주는데 왜 만료가 됐을까 궁금했다.</p>
<p>명령어로 인증서 기한을 확인해보니 갱신은 올바르게 이루어지고 있었다.</p>
<p>바로 서브 모듈에 있는 인증서가 만료가 된 것이다. 인증서 갱신은 당연히 <code>live 디렉토리</code>에 있는 <code>pem키</code>를 갱신하는 것이다.
따라서 지금과 같은 방식은 만료전에 또 스프링 프로젝트로 옮겨줘야 한다.</p>
<p>보통 블로그에서는 스프링에 인증서를 옮긴 후에 https접속을 확인한다.
그리고 인증서 자동 갱신 명령어를 crontab에 적어준다.</p>
<p>생각없이 따라만하니 인증서가 자동갱신이 되는구나 하고 넘겼던 것이다. </p>
<p>일단 키를 로컬로 옮긴후 또 서브모듈에 넣어준 후 빌드를 하면 당장은 해결된다.
하지만 아무리 3개월이라도 매번 옮기는 것보다는 더 좋은 방법을 찾아야한다.(오히려 3개월이면 잊어버릴 것 같다.)</p>
<hr>
<p>이해없이 따라만하는 것은 개발 환경 설정에도 코드 작성에도 매우 위험한 습관인 것 같다.
다음 포스팅으로는 https에 대해 알아보고 위 문제를 어떻게 해결할 지에 대한 내용으로 작성할 것이다.</p>
<p><a href="https://velog.io/@gourd_erased/%ED%81%B4%EB%B0%8B-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90%EC%84%9C-SSL-%EC%9D%B8%EC%A6%9D%EB%B0%9B%EC%9C%BC%EB%A9%B0-%EC%83%9D%EA%B8%B4-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0">해결과정</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[클밋] 스프링 프로파일 설정을 통한 개발환경 분리(도커와 깃허브 액션)]]></title>
            <link>https://velog.io/@gourd_erased/%ED%81%B4%EB%B0%8B-%EC%8A%A4%ED%94%84%EB%A7%81-%ED%94%84%EB%A1%9C%ED%8C%8C%EC%9D%BC-%EC%84%A4%EC%A0%95%EC%9D%84-%ED%86%B5%ED%95%9C-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EB%B6%84%EB%A6%AC%EB%8F%84%EC%BB%A4%EC%99%80-%EA%B9%83%ED%97%88%EB%B8%8C-%EC%95%A1%EC%85%98</link>
            <guid>https://velog.io/@gourd_erased/%ED%81%B4%EB%B0%8B-%EC%8A%A4%ED%94%84%EB%A7%81-%ED%94%84%EB%A1%9C%ED%8C%8C%EC%9D%BC-%EC%84%A4%EC%A0%95%EC%9D%84-%ED%86%B5%ED%95%9C-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EB%B6%84%EB%A6%AC%EB%8F%84%EC%BB%A4%EC%99%80-%EA%B9%83%ED%97%88%EB%B8%8C-%EC%95%A1%EC%85%98</guid>
            <pubDate>Mon, 22 Jan 2024 06:53:02 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>클라이밍 앱 &#39;클밋&#39;을 제작하며 고민/개발 내용을 기록한 글입니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/c3a0b248-1f21-4243-b83a-fa6b11d1d66f/image.png" alt=""></p>
<h3 id="yml-파일-지웠다가-썼다가">yml 파일 지웠다가 썼다가</h3>
<p>스프링 프로젝트를 진행할 때 보통 환경별 분리된 DB를 사용하거나 스키마 또는 테스트용으로 로컬db를 사용하기도 한다.
이전에는 로컬테스트로 localhost:3306 썼다가 잘되면 다시 엔드포인트를 적었다.만약 연관관계나 db컬럼 수정시에는 다시 또 로컬로 테스트를 하기 위해 엔드포인트를 지우고 localhost를 적는다. </p>
<p>이번 프로젝트에서는 편하게 하기 위해 <code>스프링 프로파일</code>을 적용했다.</p>
<h3 id="스프링-프로파일profile">스프링 프로파일(profile)</h3>
<p>프로필? 프로파일? 프로파일이라고 더 많이 부르는 것 같다. 프로파일은 애플리케이션 구성의 일부를 분리, 특정 환경에서만 사용하도록 하는 방법을 제공한다고한다. 일반적으로는 <code>spring.profiles.active</code>속성으로 프로필을 지정할 수 있다.</p>
<pre><code class="language-yml">spring:
  profiles:
    active: &quot;dev,hsqldb&quot;</code></pre>
<p>또는 명령줄을 통해 지정이 가능하다.</p>
<p><code>$ java -jar -Dspring.profiles.active=production demo-0.0.1-SNAPSHOT.jar</code></p>
<hr>
<h3 id="yml-파일-분리">yml 파일 분리</h3>
<p>yml파일은 아래와 같이 분리가 가능하다.</p>
<pre><code class="language-yml">server:
    port: 8080
---

spring:
    profiles: development
    datasource:
        url: &quot;개발용 db&quot;
server:
    port: 8081

---

spring:
    profiles: production
    datasource:
        url: &quot;배포용 db&quot;
server:
    port: 8082</code></pre>
<p>공식문서에서는 위로 예시를 들었다. 하지만 시큐리티, jwt 설정등이 추가되어 한 환경에 50~60줄이 넘기때문에 파일 별로 나누는 방식을 선택했다.</p>
<p>나누는 방법은 <code>application-{환경이름}.yml</code>파일 이름을 지으면 된다.</p>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/6b259fc6-a8e5-4bbe-9a14-a76f0499c46e/image.png" alt=""></p>
<p>application.yml에는 profile과 공통 설정을 적었다.
(지정한 프로파일이 우선적용이니 겹치는 내용이 있다면 조심하자)</p>
<pre><code class="language-yml">spring:
  profiles:
    active: ${profile}
  mvc:                     
    pathmatch:
      matching-strategy: ant_path_matcher
  servlet:
    multipart:
      max-request-size: 200MB
      max-file-size : 200MB
springdoc:
  swagger-ui:
    path: /
    operations-sorter: alpha
    display-request-duration: true</code></pre>
<p>dev와 prod에는 db, s3, jwt, security등 환경별로 다른 설정값들을 넣어주었다.</p>
<h3 id="로컬에서-프로파일-선택하기인텔리제이">로컬에서 프로파일 선택하기(인텔리제이)</h3>
<p>로컬의 경우 선택하는 것은 간단한다. 
profiles안에 원하는 프로파일을 넣어주면 된다.
<img src="https://velog.velcdn.com/images/gourd_erased/post/b760d18d-2adf-43e5-b780-fed22b027d4f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/a00e1200-c251-4d2b-ad3a-98c6142c0707/image.png" alt=""></p>
<p>또는 직접 빌드 후 원하는 프로파일로 실행시켜도 된다.
<code>$ java -jar -Dspring.profiles.active=dev demo-0.0.1-SNAPSHOT.jar</code></p>
<hr>
<h3 id="배포시-프로파일-지정은">배포시 프로파일 지정은?</h3>
<h4 id="도커파일">도커파일</h4>
<p>지금처럼 로컬에서는 인텔리제이로 몇 번의 <code>딸깍</code>과 한 줄 입력으로 지정이 간단한다.
하지만 배포시에는 환경을 어떻게 지정해줄까?
이전에는 단순히 프로파일 구분없이 ENTRYPOINT를 통해 실행시켜주었지만 이제는 dev와 prod를 나누어야한다.</p>
<pre><code>ENTRYPOINT [&quot;java&quot;, &quot;-jar&quot;, &quot;-Dspring.profiles.active=dev&quot;]
ENTRYPOINT [&quot;java&quot;, &quot;-jar&quot;, &quot;-Dspring.profiles.active=prod&quot;]</code></pre><p>처음에는 이 두 줄을 if문으로 나눌까했다. 그러기 위해서는 profile이 어떤것인지 받는 인자가 있어야하고 또  그렇다면 굳이 if로 나눌 필요없이 active= 에 넣어줄 수 있지 않을까?라는 생각을 했고 관련 내용을 찾아보았다.</p>
<p><a href="https://docs.docker.com/build/guide/build-args/">도커 빌드 공식문서 - build-args</a></p>
<pre><code class="language-c">+ ARG GO_VERSION=1.21
+ FROM golang:${GO_VERSION}-alpine AS base</code></pre>
<p>공식문서에는 Go언어의 버전을 지정하는 예제를 사용해 설명해주고있다. 
도커 이미지 빌드시 ARG키워드는 FROM문으로 삽입되고,  만약 GO_VERSION에 대한 빌드 인자가 없으면 기본값인 1.21로 설정된다고한다.</p>
<p>인자값을 주기 위해서는 <code>--build-arg=&quot;값&quot;</code>으로 넣어주고 <code>${변수}</code>로 넣으면 된다.</p>
<p>위 방법을 적용한 도커파일이다.</p>
<pre><code class="language-yml">FROM openjdk:17-oracle

ARG JAR_FILE=build/libs/*.jar                    //빌드된 JAR 파일의 위치를 지정
COPY ${JAR_FILE} app.jar                         // JAR파일을 Docker 이미지 안으로 복사

ENTRYPOINT [&quot;java&quot;, &quot;-jar&quot;, &quot;-Dspring.profiles.active=${SPRING_PROFILE}&quot;, &quot;/app.jar&quot;] // 컨테이너 시작시 명령어 지정</code></pre>
<p>ENTRYPOINT는 컨테이너가 시작하면 환경 변수인 SPRING_PROFILE을 받아 프로파일을 설정 후 실행하도록 정의했다.</p>
<br>

<h4 id="깃허브-액션">깃허브 액션</h4>
<p>그럼 도커이미지 빌드시 도커파일에 매개변수는 어떻게 주입을 해야할지 알아보자.
빌드하는 과정을 브랜치 기준으로 if문을 통해 나눴다. github.ref_name은 공식문서를 참고했고 트리거가 된 브랜치명을 담고 있다.
<a href="https://velog.io/@gourd_erased/%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%95-%EC%84%9C%EB%B2%84%EC%99%80-%EC%9A%B4%EC%98%81-%EC%84%9C%EB%B2%84%EB%A5%BC-%EB%82%98%EB%88%A0-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0">왜 브랜치 기준으로 환경을 나누나요?</a></p>
<p>빌드시에 <code>--build-arg=&quot;값&quot;</code> 인자값을 지정해 도커파일 내부로 값을 전달해주었다.</p>
<pre><code class="language-yml">      # Docker 이미지 빌드 (Main Branch)
      - name: Docker 이미지 빌드 (Main Branch)
        if: github.ref_name == main
        run: docker build --build-arg SPRING_PROFILE=prod -t gourderased/spring-project:latest .

      # Docker 이미지 빌드 (Release Branch)
      - name: Docker 이미지 빌드 (Release Branch)
        if: github.ref_name == release&#39;
        run: docker build --build-arg SPRING_PROFILE=dev -t gourderased/spring-project:latest .</code></pre>
<p>이후 컨테이너가 실행되면 위 도커파일에 prod 또는 dev가 전달되어 배포시에도 프로파일 설정이 가능해졌다.</p>
<hr>
<h3 id="마치며">마치며</h3>
<p>설정 자체는 크게 어렵지 않았지만 도커에 대한 이해가 더욱 필요함을 느꼈다. 이번 글을 정리하면서 도커를 실행하는 것과 빌드할 때가 각각 언제인지 명확하지 않았는데 확실히 알게 되었다. 깃허브 액션의 경우에는 지원해주는 기능이 생각보다 많았다.
다음번에는 가능하다면 슬랙과 연동해 action이 수행하는동안 기다리지 않고 알림을 통해 성공 여부를 확인하는 스크립트를 작성하고 싶다.</p>
<hr>
<h3 id="출처">출처</h3>
<ul>
<li>스프링 프로파일 : <a href="https://docs.spring.io/spring-boot/docs/1.2.0.M1/reference/html/howto-properties-and-configuration.html#howto-change-configuration-depending-on-the-environment">docs.spring.io - 59. Properties &amp; configuration</a></li>
<li>도커 build-agrs : <a href="https://docs.github.com/en/actions/learn-github-actions/contexts#github-context">docs.github.com - Contexts</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[클밋] 스테이징 서버와 운영 서버를 나눠 배포하기]]></title>
            <link>https://velog.io/@gourd_erased/%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%95-%EC%84%9C%EB%B2%84%EC%99%80-%EC%9A%B4%EC%98%81-%EC%84%9C%EB%B2%84%EB%A5%BC-%EB%82%98%EB%88%A0-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@gourd_erased/%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%95-%EC%84%9C%EB%B2%84%EC%99%80-%EC%9A%B4%EC%98%81-%EC%84%9C%EB%B2%84%EB%A5%BC-%EB%82%98%EB%88%A0-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 22 Jan 2024 06:12:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>클라이밍 앱 &#39;클밋&#39;을 제작하며 고민/개발 내용을 기록한 글입니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/315c4979-c9a8-4002-b0b7-dcf47f1b10ea/image.png" alt=""></p>
<h3 id="이전-방식">이전 방식</h3>
<p>보통 내가 했던 배포 방식은 <code>develop</code> 브랜치에서 주차별 혹은 어느정도의 api가 모이면 바로 <code>main</code> 에 보내는 방식을 썼다. 
<code>main</code> 에 <code>push</code> 가 되면 깃허브 액션을 통해 바로 서버에 배포했다. 웹을 배포한 후에도 이 방식을 사용해 각자 로컬에서 테스트된 브랜치들을 develop에 모아놨다가 배포된 서버로 보냈다.</p>
<p>하지만 develop에서 충돌 해결하는 과정에서 생기는 컴파일 에러 혹은 배포 과정에서 에러가 발생하는 경우가 있다.
이 경우 모든 프론트 팀원이 스웨거나 포스트맨으로 테스트 자체를 못하게 된다.
만약 서비스가 배포됐다면 작은 실수로 서비스 자체가 먹통이 되는 경우가 생기게 된다.</p>
<p>이를 막고자 두 서버의 환경을 분리하고자 한다.</p>
<hr>
<h3 id="스테이징-서버와-운영-서버">스테이징 서버와 운영 서버</h3>
<p>스테이징 서버란 실제 운영 서버로 배포되기 전에 애플리케이션을 테스트하고 검증하는 역할을 하는 서버를 말한다.
운영 서버로 배포 전 테스트를 하는 것이므로 <code>완벽히 동일한 환경</code>을 구성하는 것이 중요하다.</p>
<p>운영서버는 말그대로 실제 유저가 사용하는 서버이다.
스테이징 서버에서 테스트한 브랜치를 그대로 운영 서버에 배포를 하면된다.</p>
<p>서버를 분리할 경우 깃 브랜치는 어떻게 관리를 해야할까?</p>
<hr>
<h3 id="깃-전략">깃 전략</h3>
<p>우리 팀의 <em>PM</em> 분께서 Git-flow 전략을 제시했다. 확실히 Github-flow보다 복잡했지만 QA에 대한 필요성을 느끼고 있었기에 따르기로 했다.
<a href="https://angel927.tistory.com/149">[번역]정말 프로젝트를 시작할 때부터 QA가 필요할까 - 검은 왕자 블로그</a></p>
<p>그럼 브랜치는 크게 3가지가 존재한다.</p>
<ul>
<li>develop</li>
<li>release</li>
<li>main</li>
</ul>
<p>여기서 <code>release</code> 브랜치를 통해 <code>develop</code> 브랜치와 분리 후 스테이징 서버 배포를 하게 된다. 
<code>develop</code> 은 <code>feature</code> 개발 완료된 브랜치들을 계속 통합한다. <code>release</code> 브랜치에서는 <code>main</code>에 배포되기 전 QA를 하며 버그 발견시 바로 브랜치에서 직접 수정을 한다.</p>
<hr>
<h3 id="서버와-데이터베이스">서버와 데이터베이스</h3>
<p>이제는 두 분리된 환경에서 각각 서버와 데이터베이스는 어떻게 해야할지 고민해보자</p>
<h4 id="서버">서버</h4>
<p>이전 서브 도메인을 배울때 prod.climeet.com 과 test.climeet.com처럼 나눈다고 배웠어서 하나의 Ip(서버)에서 두 환경을 모두 구동하는 것인가 고민했다.</p>
<p>먼저 분리 고민 이전에 프리티어 EC2에서 2개의 스프링을 돌릴 수 있는지 확인했다. 도커를 통해 실행하기에 <code>docker stats</code>로 확인을 했다.</p>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/45615171-bdcd-4a0d-b66e-2a2f0febfdc2/image.png" alt=""></p>
<p>요청이 아예 없을 때 300MB이고, ec2 프리티어는 1G의 메모리를 가진다. 스왑 메모리를 사용해도 주 메모리에 비해 속도가 느리기 때문에 2개를 돌리는 것은 무리라고 판단했다.</p>
<p>그리고 무엇보다 api응답 속도와 같이 성능 테스트를 할 경우 서로 영향을 끼칠 수 있기에 환경을 아예 분리시키는 것이 낫다고 판단했다. 그래서 동일한 환경의 ec2 2개를 돌리기로 했다.</p>
<h4 id="db">DB</h4>
<p>DB의 경우 RDS는 스키마를 통해 분리하려했지만, 위와 동일한 이유로 환경을 아예 격리하는 것이 나을 것같아 각각 ec2가 하나씩 연결되게 했다.S3 또한 분리했다.</p>
<p>내가 참고한 <code>라인 게임 기술 블로그</code>에서는 스테이징 이전에 베타 서버를 두었고 staging서버와 prod서버가 같은 DB를 연결해두었다.우리 프로젝트에서는 테스트를 할 수 있는 곳이 staging밖에 없어 분리가 낫다 판단했다.</p>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/f7354fa8-7d6e-4d68-a0d5-cab710a647dd/image.png" alt=""></p>
<p>팀원들과 회의를 하며 간단히 도식화를 해봤다.</p>
<blockquote>
<p>더 알아봐야겠지만 정말 정확한 테스트를 위해서는 앞서 말했듯이 staging이전에  분리된 db를 갖는 서버를 두어 테스트를 진행한 후 staging과 prod서버가 동일한 db를 갖는 것이 맞는 것 같다.</p>
</blockquote>
<hr>
<h3 id="배포">배포</h3>
<p><code>깃허브 액션</code> 을 통한 <code>도커</code> 배포를 자동화했다.
먼저 빌드 후 테스트를 진행한다.
이후 두 어느 브랜치이냐에 따라 도커 이미지를 프로필에 맞춰 빌드한다. 이후 브랜치에 따른 ec2에 접속해 빌드한 도커 이미지를 pull받아 실행한다.</p>
<pre><code class="language-yml">FROM openjdk:17-oracle

ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar

ENTRYPOINT [&quot;java&quot;, &quot;-jar&quot;, &quot;-Dspring.profiles.active=${SPRING_PROFILE}&quot;, &quot;/app.jar&quot;]</code></pre>
<pre><code class="language-yml">name: CLIMEET_DEPLOY

on:
  push:
    branches: [&quot;main&quot;, &quot;release&quot;]
jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      #체크아웃
      - name: Checkout code
        uses: actions/checkout@v3

      # JDK 설치
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: &#39;17&#39;
          distribution: &#39;temurin&#39;

      #서브 모듈 접근
      - name: Checkout repo
        uses: actions/checkout@v3
        with:
          token: ${{ secrets.CLIMEET_ACTION_TOKEN }}
          submodules: true

      # 서브 모듈 변경 점 있으면 update
      - name: Git Submodule Update
        run: |
          git submodule update --remote --recursive

      # gradlew 권한 변경
      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      # 빌드(test는 제외) :아직 테스트 코드를 만들지 않아 제외했음 추후 변경
      - name: Build with Gradle
        uses: gradle/gradle-build-action@v2
        with:
          arguments: clean build -x test

      # Docker 이미지 빌드 (Main Branch)
      - name: Docker 이미지 빌드 (Main Branch)
        if: github.ref == &#39;refs/heads/main&#39;
        run: docker build --build-arg SPRING_PROFILE=prod -t gourderased/spring-project:latest .

      # Docker 이미지 빌드 (Release Branch)
      - name: Docker 이미지 빌드 (Release Branch)
        if: github.ref == &#39;refs/heads/release&#39;
        run: docker build --build-arg SPRING_PROFILE=dev -t gourderased/spring-project:latest .

      # DockerHub 로그인
      - name: Docker - Login to DockerHub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      # Docker Hub 이미지 푸시
      - name: Docker Hub 퍼블리시
        run: docker push gourderased/spring-project:latest

      # Deploy to EC2 (Main Branch)
      - name: Deploy to EC2 (Main Branch)
        if: github.ref == &#39;refs/heads/main&#39;
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.MAIN_EC2_HOST }}
          username: ubuntu
          key: ${{ secrets.MAIN_SSH_KEY }}
          script: |
            sudo docker stop $(sudo docker ps -a -q)
            sudo docker rm $(sudo docker ps -a -q)
            sudo docker pull gourderased/spring-project:latest
            sudo docker run -d -p 8080:8080 --name climeet-prod-server gourderased/spring-project:latest

      # Deploy to EC2 (Release Branch)
      - name: Deploy to EC2 (Release Branch)
        if: github.ref == &#39;refs/heads/release&#39;
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.RELEASE_EC2_HOST }}
          username: ubuntu
          key: ${{ secrets.RELEASE_SSH_KEY }}
          script: |
            sudo docker stop $(sudo docker ps -a -q)
            sudo docker rm $(sudo docker ps -a -q)
            sudo docker pull gourderased/spring-project:latest
            sudo docker run -d -p 8080:8080 --name climeet-dev-server -e SPRING_PROFILE=dev gourderased/spring-project:latest
</code></pre>
<p>ec2는 분리되어있으니 배포또한 따로 해야한다.
그렇다면 도커의 이미지는 왜 분리해야할까? 바로 스프링 프로파일때문이다. </p>
<blockquote>
<p>스프링은 실행시 profile에 dev또는 local, prod등을 넣어주면 해당 프로필에 맞는 yml을 찾아 실행시킨다.</p>
</blockquote>
<p>자세한건 아래 포스트를 참고하자.
<a href="https://velog.io/@gourd_erased/%ED%81%B4%EB%B0%8B-%EC%8A%A4%ED%94%84%EB%A7%81-%ED%94%84%EB%A1%9C%ED%8C%8C%EC%9D%BC-%EC%84%A4%EC%A0%95%EC%9D%84-%ED%86%B5%ED%95%9C-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EB%B6%84%EB%A6%AC%EB%8F%84%EC%BB%A4%EC%99%80-%EA%B9%83%ED%97%88%EB%B8%8C-%EC%95%A1%EC%85%98">스프링 프로파일 통한 환경분리</a></p>
<h3 id="마치며">마치며</h3>
<p>만약 이렇게 구성을 한다면 스케일링 업/다운시에도 똑같이 해줘야하는거 아닌가 생각이 든다.
환경 분리를 하고 관리하는 것은 시간과 노력이 들지만 QA나 추후 기능 추가, 리팩토링시 매우 큰 도움이 될 수 있을 것 같다.</p>
<h4 id="출처">출처</h4>
<p><a href="https://blog.naver.com/linegamedev/220068468083">[LINE Rangers 신입사원의 서버 분석기] - 개발 환경</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] ArrayList.Contains() 동작 원리]]></title>
            <link>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-ArrayList.Contains-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC</link>
            <guid>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-ArrayList.Contains-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC</guid>
            <pubDate>Tue, 24 Oct 2023 11:04:33 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우테코 6기 프리코스를진행하며 contains 메소드를 공부한 경험을 공유해보고자 한다.</p>
</blockquote>
<p>요구사항에 따르면 랜덤으로 생성한 숫자 3개가 중복이 되면 안된다. </p>
<p>처음에 나는 반복문을 돌며 이전에 생성한 수를 <code>모두 탐색</code> 하는 방법을 생각했다. 하지만 숫자 3개가 아닌 더 커지게 된다면 탐색 시간이 증가한다. 그래서 좋은 코드가 아니라 생각해 변경했다.</p>
<p>내가 짠 코드는 다른 방식으로 중복을 확인했다.</p>
<pre><code class="language-java">    //중복을 확인할 배열 생성
  boolean[] isNumberUsed = new boolean[9];

  //수 생성하기
  while (targetNumbers.size() &lt; 3) {
      int generatedNumber = Randoms.pickNumberInRange(1, 9);
      //만약 중복되면 다시 생성
      if (isNumberUsed[generatedNumber - 1]) {
          continue;
      }
      targetNumbers.add(generatedNumber);
      isNumberUsed[generatedNumber - 1] = true;</code></pre>
<p>먼저 크기 9의 배열을 생성한다. 이후 수를 생성하면 배열의 해당 인덱스를 true로 바꾼다. 만약 이미 true라면 중복이라는 뜻이므로 while문을 다시 돌며 수를 재생성한다.</p>
<p>이 코드는 수 생성시 배열을 처리하는 과정이 있지만 중복 확인 과정에서의 시간을 단축시킬 수 있다. </p>
<hr>
<h3 id="readme-잘-읽자">Readme 잘 읽자….</h3>
<p>이후 다음 기능 구현을 위해 Read.md를 읽던 나는 빠뜨린 부분을 발견했다.</p>
<p>우테코에서 친절히 라이브러리 사용방법을 알려주며 코드를 제공해주었던 것이다. </p>
<pre><code class="language-java">List&lt;Integer&gt; computer = new ArrayList&lt;&gt;();
while (computer.size() &lt; 3) {
    int randomNumber = Randoms.pickNumberInRange(1, 9);
    if (!computer.contains(randomNumber)) {
        computer.add(randomNumber);
    }
}</code></pre>
<p>Randoms 사용방법만 확인한 나는 <code>contain</code> 을 늦게 확인했다…🥲</p>
<p>코드가 훨씬 가독성있고 간결해보인다.</p>
<p>하지만 그냥 넘어가기에는 <code>contain</code> 메소드의 경우에는 또 어떤 방식으로 해당 요소를 찾는지 궁금해져 알아봤다.</p>
<hr>
<h3 id="arraylistcontains란">ArrayList.contains()란</h3>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/3ecb4176-a953-44d9-b358-2ca57d3d55a3/image.png" alt=""></p>
<p><code>contains()</code>는 리스트 안 어떤 객체가 있는지 확인하는데 사용하는 메소드이다.</p>
<p>만약 리스트에 그 객체가 존재하면 <code>boolean true</code> 값을 반환한다.</p>
<p>이제 속을 들여다보자</p>
<h3 id="contain-내부-들여보기">contain 내부 들여보기</h3>
<p>위 주석을 보면 <code>equals</code>를 사용해 최소 하나 이상 있다면 true를 반환하는 것을 알 수 있다.</p>
<p>더 자세히 보기 위해 <code>indexOf</code>도 살펴보자</p>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/375c799a-9bef-4c2e-88cb-a481b46a7ab1/image.png" alt=""></p>
<p><code>indexOf</code> 의 주석을 보면 리스트의 첫번째 출현 인덱스를 반환한다. 리스트를 순회하다 <code>o.equals(es[i])</code>가 <code>true</code>가 되면 바로 return을 하는 것이다. </p>
<p>따라서 찾게 되면 적어도 인덱스가 0보다 크게 되기 때문에 <code>contains</code>함수에서 이를 이용할 수 있는 것이다. </p>
<hr>
<h3 id="그래서">그래서..?</h3>
<p><code>중복 확인 배열</code>의 경우 <strong>O(1)</strong>이 걸리지만 <code>contains</code>의 경우 <strong>O(n)</strong>의 시간이 걸린다.</p>
<p>하지만…!</p>
<p>글을 작성하며 정답 수는 최대 <strong>9자리</strong>라는 걸 깨달았다. </p>
<p>따라서 요구사항의 변경을 고려한 코딩을 하더라도 성능에는 큰 차이가 없다. </p>
<p>(모두 탐색해도 배열의 크기가 최대 9이기 때문)</p>
<p>가독성이 있는 <code>contains</code>를 사용하는 것이 더 좋을 것 같다. </p>
<p>그동안 메소드가 어떻게 돌아가는지 크게 생각안하고 사용을 했었다.</p>
<p>앞으로는 무조건 쓰는 것이 아닌 메소드의 내부 동작을 알고 다른 방법도 고려하며 사용해야 할 것 같다.</p>
<p><strong><del>(+ readme를 잘읽고 최대 범위도 미리 잘 생각하자…ㅎ)</del></strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] 메모리 관리(쓰레기 수집가)]]></title>
            <link>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B4%80%EB%A6%AC%EC%93%B0%EB%A0%88%EA%B8%B0-%EC%88%98%EC%A7%91%EA%B0%80</link>
            <guid>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B4%80%EB%A6%AC%EC%93%B0%EB%A0%88%EA%B8%B0-%EC%88%98%EC%A7%91%EA%B0%80</guid>
            <pubDate>Sun, 20 Aug 2023 14:53:53 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/gourd_erased/post/455bffae-1c0c-4b6a-85eb-4b4454ef4b0c/image.png" alt=""></p>
<p>코테를 c++로 하면서 메모리 관련 버그가 나고, malloc과 free를 쓸 때마다 자바의 메모리 관리에서의 편리함을 느낀다. 
자바는 C++과 달리 메모리를 직접 비우지 않고 가비지 컬렉션을 사용하는데 어떤 방식으로 사용되는지 알아보자.</p>
<p><a href="https://www.zdnet.com/article/microsoft-70-percent-of-all-security-bugs-are-memory-safety-issues/">Microsoft 버그의 70%는 메모리 관련 버그이다.</a></p>
<h3 id="가비지-콜렉션이란">가비지 콜렉션이란?</h3>
<p>프로그램 개발시 <strong>유효하지 않은 메모리</strong>인 가비지가 발생한다. </p>
<pre><code class="language-java">
public class Example {
    public static void main(String[] args) {
        String str1 = new String(&quot;junsik&quot;);
        String str2 = new String(&quot;bex&quot;);

        str1 = str2; // str1은 이제 &quot;bex&quot;를 가리킨다.
    }
}</code></pre>
<p>위 예시에서 &quot;junsik&quot;에 대한 참조가 없어져 가비지가 된다.
java는 가비지 컬렉터(GC)가 주기적으로 검사해 메모리를 청소해준다.</p>
<hr>
<h3 id="gc의-청소-방식">GC의 청소 방식</h3>
<h4 id="mark-and-sweep">Mark And Sweep</h4>
<p>가비지 컬렉션이 동작하는 기초적인 청소 과정이다.</p>
<p>원리는 대상 객체를 <strong>식별</strong>, <strong>제거</strong>하며 이후 파편화된 메모리를 앞에서부터 채워나가는 작업을 수행한다. 위에 봤듯이 참조되지 않는 대상을 청소해야하는데 어떻게 알 수 있을까?
바로 그래프를 이용한다. </p>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/fe75845a-6f48-4eaf-9bae-e25ee63ac639/image.png" alt=""></p>
<ul>
<li>Mark(식별) : 먼저 메모리의 Root로부터 그래프 순회를 통해 연결된 객체를 가각 찾아내어 참조하는 객체를 마킹한다.</li>
<li>Sweep(제거) : 참조되지 않은 객체(Unreachable)들을 Heap에서 제거한다. 파란색 객체들을 제거하는 것이다.</li>
<li>Compact : Sweep 이후 분산된 객체들을 Heap 시작주소로 모아 메모리가 할당된 부분과 아닌 부분으로 압축한다.</li>
</ul>
<hr>
<h3 id="동작-과정">동작 과정</h3>
<h4 id="java의-heap">java의 heap</h4>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/408700d8-c431-4cc5-9501-047452069328/image.png" alt=""></p>
<ul>
<li>new 키워드로 생성된 객체와 배열이 생성되는 영역이다.</li>
<li>주기적으로 GC가 제거하는 영역이다.</li>
</ul>
<p>Young Generation 영역은 객체가 생성되고 얼마 안됐을 때 저장되는 공간이다.힙에 생성 시 최초 Eden 영역에 할당된다. 이후 어느 정도 데이터가 쌓이게 되면 참조 정도에 따라 survivor의 빈 공간으로 이동되거나 회수된다.
위 Old로 이동되거나 회수되는 것을 <strong>Minor GC</strong> 라고 한다. Old영역은 Tenured Generation이라고도 불리고 종신직, 임기의 뜻을 갖고 있다.</p>
<p>Old 영역에 할당된 메모리가 허용치를 넘으면 Old에 모든 객체를 검사하여 참조되지 않는 객체들을 한꺼번에 삭제하는 GC가 실행된다. 이때 GC실행 스레드를 제외한 모든 스레드는 작업을 멈추므로 이를 <strong>Stop-the-World</strong>가 발생한다고 하고 Old영역의 메모리를 회수하는 CG를 <strong>Major CG</strong>라고 한다.</p>
<h3 id="알고리즘">알고리즘</h3>
<p>Heap의 사이즈가 자바의 발전에 맞춰 점차 커지면서 최적화를 위해 GC 알고리즘이 개발되었다.
상황에 따라 적용이 가능하다.</p>
<h4 id="serial-gc">Serial GC</h4>
<ul>
<li>서버의 CPU 코어가 1개일 때 사용하는 단순한 GC</li>
<li>GC 스레드가 1개이므로 stop-the-world가 가장 길다.</li>
<li>CPU 코어가 1개인 경우가 아니라면 사용하지 말자!</li>
</ul>
<h4 id="parallel-gc">Parallel GC</h4>
<ul>
<li>Java8의 디폴트 GC이다.</li>
<li>Serial GC와 기본적인 개념은 같지만,  Youn 영역의 Minor GC를 멀티 스레드로 수행한다.</li>
</ul>
<h4 id="parallel-old-gc">Parallel old GC</h4>
<ul>
<li>위 Parallel GC에서 Old영역도 멀티 스레드로 GC를 수행하도록 한 것이다.</li>
</ul>
<h4 id="cms-gcconcuurent-mark-sweep">CMS GC(Concuurent Mark Sweep)</h4>
<ul>
<li>어플리케이션 스레드와 GC 스레드가 동시 실행되는 것이다.</li>
<li>GC과정이 매우 복잡하고 CPU, 메모리 파편화 등 여러 문제로 Java 14에서는 사용이 중지되었다.</li>
</ul>
<h4 id="g1-gc">G1 GC</h4>
<ul>
<li>CMS를 대체하기 위해 나왔다.</li>
<li>Heap이 너무 작을경우 미사용 권장된다.</li>
<li>G1 gc는 young/Old 영역을 나누지 않고 Region이라는 개념을 도입하여 사용한다. 힙 내부 영역을 동적으로 부여해 GC빈도를 줄인다.</li>
</ul>
<h4 id="z-gc">Z GC</h4>
<ul>
<li>Java15에 릴리즈</li>
<li>대량의 메모리를 처리하기 위해 나왔다.</li>
<li>ZPage영역을 사용, 힙 크기가 증가하더라도 &#39;stop-the-world&#39;의 시간이 절대 10ms를 넘지 않는다고한다.</li>
</ul>
<hr>
<h3 id="gc-단점">GC 단점</h3>
<ul>
<li>개발자는 메모리가 언제 해제되는지 정확히 알 수 없다.</li>
<li>GC 동작 중에 다른동작을 멈추므로 오버헤드가 발생한다.</li>
</ul>
<p>GC가 c++에 비해 편리할 수 있지만 오버헤드가 일어날 수 있다. GC가 있다하더라도 개발자에게 메모리 관리는 항상 신경써야하는 문제인 것 같다...!!</p>
<hr>
<p><a href="https://mangkyu.tistory.com/118">티스토리(망나니개발자)</a>
<a href="https://velog.io/@jkijki12/Java-%EA%B0%80%EB%B9%84%EC%A7%80%EC%BB%AC%EB%A0%89%ED%84%B0">벨로그(YoungHo-Cha)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] Obejct class]]></title>
            <link>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-Obejct-class</link>
            <guid>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-Obejct-class</guid>
            <pubDate>Tue, 15 Aug 2023 09:32:03 GMT</pubDate>
            <description><![CDATA[<h3 id="obejct가-뭐야">Obejct가 뭐야</h3>
<p>가끔 지피티에게 자바 코드를 질문하면 자료형을 Object로 하는 코드를 제시해줄 때가 종종있다. 현재 진행중인 프로젝트에서 &#39;리우&#39;가 추가한 <strong>MBTI별 게시글 수 조회</strong>코드도 Object를 사용했다. Object를 알아보면서 아래 코드를 이해해보자.</p>
<pre><code class="language-java">BoardList getBoardCountRes() { 
    BoardList boardList = new BoardList();
    //전체 게시글 개수 설정
    boardList.setBoardCount(boardRepository.countAllByStateTrue());
    //MBTI별 게시글 개수 설정
    List&lt;Object[]&gt; boardCountList = boardRepository.countBoardsByMbti();

    for(Object[] row: boardCountList) {
        MbtiEnum mbti = (MbtiEnum) row[0];
        Long count = (Long) row[1];

        switch(mbti) {
            case INFJ -&gt; boardList.setINFJ(count);
            ...
        }
    }
    return boardList;
}</code></pre>
<h3 id="object-클래스">Object 클래스</h3>
<p>모든 클래스의 최상위 클래스이다. Obejct 클래스는 필드가 없고 메소드로 구성되어 있다. 이 클래스의 메소드는 어떤 클래스에서도 호출이 가능하다.
자주 쓰는 메소드를 알아보자</p>
<h4 id="equals">equals()</h4>
<p>모든 객체가 매개 값이 될 수 있다. Object가 최상위 타입이므로 모든 객체가 타입 변환이 가능하기 때문이다. 또한 equals는 실제 데이터가 아닌 참조 값으 기반으로 비교한다. 예를 보자</p>
<pre><code>//object class의  equals 함수
public static void main(String args[])
{
        User user1 = new User(&quot;shlee&quot;, 20);
        User user2 = new User(&quot;shlee&quot;, 20);
        System.out.println(user1.equals(user2));
         // 결과는 false
}</code></pre><p>두 User객체는 필드의 값이 같아도 참조 값이 다르기 때문에 false가 출력된다.</p>
<p>Object를 오버라이딩한 String의 equals는 어떨까</p>
<ul>
<li>리터럴 비교시</li>
</ul>
<pre><code>      String aa = &quot;soju&quot;;
      String bb = &quot;soju&quot;;
      System.out.println(aa.equals(bb)); 

      //결과 true</code></pre><p>이때 자바는 <strong>문자열 리터럴 풀</strong>이라는 공간에 해당 문자열을 해당한다. 따라서 aa와 bb는 둘다 동일한 주소값을 참조하게 되므로 true가 나오게 된다.</p>
<ul>
<li>문자열 객체 비교시<pre><code class="language-java">String aa = new String(&quot;soju&quot;);
String bb = new String(&quot;soju&quot;);
System.out.println(aa.equals(bb))</code></pre>
</li>
</ul>
<p>이 경우에는 위 User를 비교할 때처럼 equals를 사용한다. 하지만  String의 equals는 true를 반환한다. 왜일까
이 경우에는 오버라이딩되어 문자열의 내용을 비교하기 때문이다!</p>
<p>equals는 주의해서 사용해야할 것 같다. 클래스 사용시에는 사용할 클래스에 오버라이딩을 해서 사용하자.</p>
<h4 id="tostring">toString()</h4>
<p>해당 인스턴스에 대한 정보를 문자열로 반환한다. 이때 반환되는 문자열은 클래스 이름과 함께 구분자로 &#39;@&#39;가 사용되며, 그 뒤로 16진수 해시 코드가 추가된다.</p>
<pre><code class="language-java">Person2@2a139a55</code></pre>
<p>이미 모든 객체가 이 메소드를 갖고 있기 때문에 재정의해서 클래스를 나타내는 용도로 사용할 수 있을 것 같다.</p>
<hr>
<h4 id="그래서-object를-왜-썼을까">그래서 Object를 왜 썼을까</h4>
<p>코드를 다시 보면 Obejct[] 반복문 안에 각각 MBTIEnum과 Long으로 형변환한 것이 보인다. 위에서 봤듯이 모든 클래스는 Object를 상속 받기 때문에 타입 변환이 자유롭다.</p>
<p>위 코드에서 <code>countsBoardByMbti</code> 쿼리는 </p>
<pre><code class="language-java">@Query(&quot;select b.mbti, count(b) from Board b where b.state = true group by b.mbti&quot;)
    List&lt;Object[]&gt; countBoardsByMbtiAndStateIsTrue();</code></pre>
<p>이렇게 되어있다. 보면 mbti와 해당 mbti별 count를 동시에 받아야하는 상황에서 object는 둘을 한번에 받을 수 있는것이다. </p>
<p>결론 : 쿼리 결과의 다양한 자료형 데이터들을 효율적으로 묶어 사용할 때에는  Object를 사용하자. </p>
<p>출처 : <a href="https://www.appletong.com/entry/JAVA-object-equals-String-equals-%EB%8A%94-%EB%AD%90%EA%B0%80-%EB%8B%A4%EB%A5%BC%EA%B9%8C">애플통 티스토리</a>
<a href="http://www.tcpschool.com/java/java_api_object">TCP SCHOOL</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] 컬렉션 이해하기]]></title>
            <link>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-%EC%BB%AC%EB%A0%89%EC%85%98-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-%EC%BB%AC%EB%A0%89%EC%85%98-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 14 Aug 2023 16:59:01 GMT</pubDate>
            <description><![CDATA[<h3 id="컬렉션이란">컬렉션이란</h3>
<p>자바에서는 자료구조와 알고리즘을 구조화하여 클래스로 구현해놓은것이다.
자바의 인터페이스를 사용해 구현된다.</p>
<h4 id="구성-요소">구성 요소</h4>
<ol>
<li>인터페이스 : 각 컬렉션을 나타낸다. (List, Set, Map) 각 클래스는 상세적인 것은 달라도 일관된 조작법으로 사용가능하다.</li>
<li>클래스 : 인터페이스의 구현이다. List여도 ArrayList, LinkedList 등으로 상세 구현이 달라진다.</li>
<li>알고리즘 : 컬렉션이 제공하는 연산, 검색, 정렬 등의 메소드</li>
</ol>
<h4 id="주요-인터페이스">주요 인터페이스</h4>
<ol>
<li>List 인터페이스</li>
<li>Set 인터페이스</li>
<li>Map 인터페이스</li>
</ol>
<p>구조상의 차이로 Map인터페이스만 별도로 정의된다.</p>
<h4 id="상속-관계">상속 관계</h4>
<p><img src="https://velog.velcdn.com/images/gourd_erased/post/d016caf7-5010-4b04-9f78-4b4710a98a52/image.png" alt=""></p>
<h4 id="특징">특징</h4>
<ul>
<li>List<E> : 순서가 있는 집합, 중복 허용(Vector, ArrayList, LinkedList, Stack, Queue)</li>
<li>Set<E> : 순서가 없는 집합, 중복 허용X (HashSet, TreeSet)</li>
<li>Map&lt;K,V&gt; : 키와 값의 한쌍 집합, 순서 없음, 키는 중복 허용하지 않지만 값은 중복 가능하다.(HashMap, TreeMap, Hashtable)</li>
</ul>
<h4 id="arraylist">ArrayList</h4>
<p>  List 인터페이스를 구현한 클래스이며 컬렉션에서 가장 많이 사용된다. 기존 vector를 개선 구현한 것이다. Vector가 있지만 기존 소스와 호환성을 위해 남겨둔 것이므로 ArrayList를 사용하자!</p>
<p>  ArrayList는 Objet배열을 이용한다. ArrayList는 벡터처럼 초과시 더 큰 배열을 생성해 새로 사용한다.또한 중요한 점은 중간 객체를 제거하면 바로 뒤부터 마지막 객체까지 모두 앞으로 idx가 1씩 앞당겨진다. 따라서 삭제연산이 많을 경우에는 LinkedList가 효율이 좋다.(읽는 접근 시간은 느리다.)</p>
<h4 id="iterator">Iterator</h4>
<p>  Iterator 인터페이스는 컬렉션에 저장된 요소를 읽어오는 방법이다.</p>
<ul>
<li><p>boolean hasNext() : 읽어올 요소가 있는지 확인</p>
</li>
<li><p>Object next() : 다음 요소 읽어오기(hasNext()로 확인 후 사용 안전)</p>
</li>
<li><p>void remove() : 읽어온 요소 삭제(next()가 선행되어야함)</p>
<pre><code class="language-java">Iterator&lt;String&gt; iterator = drinks.iterator();

    while(iterator.hasNext()){
        String str = iterator.next();
        if(str.equals(&quot;soju&quot;)){
            iterator.remove();
        }
    }
</code></pre>
<h4 id="mapk-v">Map&lt;K, V&gt;</h4>
<p>Map 인터페이스는 키와 값으로 구성된 Entry 객체를 저장한다. 해싱을 사용해 방대한 양의 데이터에서도 성능이 뛰어나다. Hashtable : Vector | HashMap : ArrayList의 관계이므로 HashMap을 사용하자. </p>
</li>
<li><p>V put(k key, V value) : key와 value저장</p>
</li>
<li><p>void clear() : 모든 데이터 삭제</p>
</li>
<li><p>V remove(Obejct key) : key와 일치하는 데이터 삭제</p>
</li>
<li><p>boolean remove(Object key, Obejct value) : key와 value가 동시에 일치하는 데이터 삭제</p>
</li>
<li><p>V replae(k key, V oldValue, V newValue) :key와 oldValue가 일치하면 newValue로 변경</p>
</li>
<li><p>V getOrDefault(Object key, V defaultValue) : key와 맵핑된 value값을 반환하고 없으면 defaultValue값을 반환</p>
</li>
<li><p>Set<K> keySet( ) : 모든 key 값을 가진 Set 데이터를 반환합니다. </p>
<ul>
<li>Collection<V> values( ) : 모든 value 값을 가진 Collection 데이터를 반환합니다. </li>
</ul>
<p>이외에 empty나 size get은 ArrayList와 동일하게 사용하면 된다.</p>
</li>
</ul>
<hr>
<p>출처 : <a href="http://www.tcpschool.com/java/java_collectionFramework_concept">TCP SCHOOL</a>
  <a href="https://velog.io/@kai6666/Java-%EC%BB%AC%EB%A0%89%EC%85%98-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-Collection-Framework">kai6666벨로그</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] 제너릭과 열거형]]></title>
            <link>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-%EC%A0%9C%EB%84%88%EB%A6%AD%EA%B3%BC-%EC%97%B4%EA%B1%B0%ED%98%95</link>
            <guid>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-%EC%A0%9C%EB%84%88%EB%A6%AD%EA%B3%BC-%EC%97%B4%EA%B1%B0%ED%98%95</guid>
            <pubDate>Sun, 13 Aug 2023 11:53:59 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/gourd_erased/post/c9939959-9850-4a18-be74-606c584074d9/image.png" alt=""></p>
<h3 id="제너릭이란">제너릭이란</h3>
<p>컴파일 시 타입을 체크해준다. </p>
<p>제너릭을 사용함으로써 잘못된 타입이 사용되는 문제를 컴파일 단에서 막을 수 있다. 실행 시 에러가 나는 것보다 컴파일 시에 미리 타입을 체크하는 것이 좋다.
또한 제너릭은 타입 변환이 필요없어 프로그램 성능이 향상된다.</p>
<pre><code class="language-java">ArrayList list = new ArrayList(); //제너릭 사용x
list.add(&quot;test&quot;);
String temp = (String)list.get(0); //타입변환 필요

ArrayList&lt;String&gt; list2 = new ArrayList(); //제너릭 사용
list2.add(&quot;test2&quot;);
temp = list2.get(0); //타입 변환 불필요</code></pre>
<h4 id="제너릭-클래스">제너릭 클래스</h4>
<pre><code class="language-java">class ExClassGeneric&lt;T&gt; {
    private T t;

    public void setT(T t) {
        this.t = t;
    }
    public T getT() {
        return t;
    }
}</code></pre>
<p>클래스 설계시 구체적 타입을 명시하지 않고 타입 파라미터로 넣어두면 ExClassGeneric<String> exGeneric = new ExClassGeneric&lt;&gt;(); 이런 식으로 타입 변환을 최소화 시킬 수 있다.
인터페이스 또한 마찬가지이다.
멀티 타입으로 &lt;K, V&gt; 두 개 이상의 파라미터도 가능하다.</p>
<hr>
<h4 id="제너릭-메소드">제너릭 메소드</h4>
<p> 제너릭 메소드는 컴파일러에게 제너릭이라는 것을 알려야한다. 따라서 제너릭 타입에 대한 정의가 반드시 필요하다.  </p>
<pre><code class="language-java">  public class ExClassGenericMethod {
    public &lt;T&gt; void showGenericInfo(T info) {
        System.out.println(info.getClass().getName() + &quot;: &quot; + info.toString());
    }
}</code></pre>
<p>  이렇게 제너릭 클래스가 아니더라도 메서드에 제너릭 타입을 사용할 수 있다.</p>
<pre><code class="language-java">  public static void main(String[] args) {
    ExClassGenericMethod exGM = new ExClassGenericMethod();
    exGM.showGenericInfo(&quot;Hello&quot;);
    exGM.showGenericInfo(10);
    exGM.showGenericInfo(10.5);
}
</code></pre>
<hr>
<h3 id="와일드-카드">와일드 카드</h3>
<p>  와일드 카드&lt;.?&gt; 
  이번 프로젝트에서 처음 본 녀석이다. 상속에서 유용하게 사용했다 </p>
<ul>
<li><p>하나의 참조 변수로 대입된 타입이 다른 객체를 참조 가능하다.</p>
</li>
<li><p>&lt;? extends T &gt; 와일드 카드의 상한 제한 T, 그 자손들만 가능하다.</p>
</li>
<li><p>&lt;? super T &gt; 와일드 카드의 하한 제한 T, 그 조상들만 가능하다.</p>
</li>
<li><p>&lt; ? &gt; 제한이 없다.</p>
</li>
<li><p>하나의 참조변수로 다양한 타입을 사용할 수 있다.</p>
<p>와일드 카드는 이번 프로젝트에서 사용했다. 아직 수정해야할 것이 남아있어서 수정 후 예시 코드로 포스팅할 예정이다.</p>
</li>
</ul>
<hr>
<h3 id="enum">Enum</h3>
<p>열거형 enum은 상수를 의밉별로 묶어 사용할 때 쓴다. 요일별로 일, 월, 화, 수...를 묶던지 mbti를 ENTJ, ENFF 등 으로 묶을 수 있다.
데이터 값의 의미를 명확히 하는 장점이 있다.</p>
<p>클래스처럼 변수와 메소드는 가질 수 있지만, 상속이나 인스턴스는 생성이 불가하다. 그리고 enum값은 상수로 public, static, final 속성을 갖고 있어 변경될 수 없다.</p>
<p>열거형은 보통 첫 글자를 대문자로 하여 시작한다.</p>
<pre><code class="language-java">    enum Week{SUN, MON, TUE ... }</code></pre>
<h4 id="사실-enum은">사실 enum은,,,,</h4>
<p>자바에서 enum은 단순히 클래스이다. enum에서의 상수는 자신의 <strong>public static final 인스턴스를 갖는 것이다.</strong></p>
<h4 id="메소드">메소드</h4>
<ul>
<li>valueOf(String str) : 문자열 str과 일치하는 열거값을 반환한다.</li>
<li>values() : 열거값 전부를 배열로 반환한다.</li>
<li>ordinal() : 열거값의 순서를 반환한다.(0부터 시작)</li>
<li>name() : enum타입의 값이 가지고 있는 문자열를 리턴한다.</li>
</ul>
<p>❗ ordinal() 사용을 자제하자.(그냥 없는 것으로 생각하라는 글도 있다.)</p>
<p>ordinal()메소드 enum상수의 위치를 나타낸다. 이는 enum이 변경되지만 않으면 상관없다. 하지만 유지보수 관점에서는 끔찍한 결과를 불러올 수 있다.정 위치값이 필요하다면 enum {SUN(0), MON(1) ...} 이런식으로 객체 필드에 별도로 정의하자!</p>
<hr>
<p>출처: <a href="http://www.tcpschool.com/java/java_api_enum">TCPschool</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] 상속과 인터페이스 ]]></title>
            <link>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-%EC%83%81%EC%86%8D%EA%B3%BC-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-pjb3nah2</link>
            <guid>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-%EC%83%81%EC%86%8D%EA%B3%BC-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-pjb3nah2</guid>
            <pubDate>Sun, 13 Aug 2023 08:27:04 GMT</pubDate>
            <description><![CDATA[<p>진행하고 있는 프로젝트에서 상속의 개념을 처음 써보게 되었다. 하지만 c++로 이론 수업만 들었던 것이 다였기 때문에 자바의 상속개념을 정리해본다.</p>
<h3 id="상속">상속</h3>
<p>상속은 부모 클래스를 자식 클래스에게 물려준다, 코드 중복을 줄일 수 있다 등 많이 알고 있을 것이다. 자바의 상속 방식을 알아보자. </p>
<p>자바 클래스 간의 상속은 여러 개의 부모를 상속하는 다중 상속 방식을 제외하고는 모두 허용한다. </p>
<ul>
<li>다단계 상속 : 부모 - 자식 - 자식 - ....</li>
<li>계층적 상속 : 부모 - 자식<pre><code>             - 자식</code></pre></li>
</ul>
<h4 id="자바의-상속-방법">자바의 상속 방법</h4>
<p><strong>extends</strong>를 사용한다. </p>
<pre><code class="language-java">class 부모class {

}
class 자식 extends 부모 {

}</code></pre>
<p>예제</p>
<pre><code class="language-java">class people{
    //필드
    String name;
    Long age;
    //메소드
    public void printMyself() {
        sout(&quot;이름: &quot; + name);
        sout(&quot;나이: &quot; + age);
    }
}

class students extends People {
    //필드
    Long drinkCapacity;

    //생성자
    Student(String name, int age, Long drinkCapacity) {
        super.name = name; // 부모필드
        super.age = age;  // 부모필드
        this.drinkCapacity = drinkcapacity;
    }

    //메소드
    public void printDrinkCapacity() {
        sout(&quot;나의 주량: &quot; + drinkCapacity);
    }
}

public class Main {
    public static void main(String[] args) {
     Student student = new Student(&quot;김준식&quot;, 20, 5);
    student.printMyself(); //부모 메소드 호출
    student.printDrinkScore(); //자식 메소드 호출
}</code></pre>
<h4 id="super-키워드">super 키워드</h4>
<p>예시 코드에서 super 키워드는 자식 클래스에서 부모 클래스를 가리킬 때 사용한다.</p>
<h4 id="부모-클래스에서-상속이-안되는-것">부모 클래스에서 상속이 안되는 것</h4>
<ul>
<li>부모 클래스의 private은 물려받을 수 없다.</li>
<li>부모와 자식 클래스가 서로 다른 패키지에 있다면 부모의 default접근 제한 을 갖는 필드 및 메소드도 물려 받을 수 없다.</li>
</ul>
<p>default 접근제한자를 잘 모르겠으면 <a href="https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-%EC%A0%91%EA%B7%BC-%EC%A0%9C%ED%95%9C%EC%9E%90%EC%99%80-staticfinal%EC%A0%95%EB%A6%AC">여기!</a></p>
<h4 id="자바의-다중-상속-불가-이유">자바의 다중 상속 불가 이유</h4>
<p>예를 들어 상속 받은 여러 개의 부모 클래스들에서 동일한 명칭의 필드나 메소드가 있다면 어떤 클래스를 상속받고 어떤 클래스에 어떻게 접근하는 지 모호함이 생긴다. 따라서 자바에서는 다중 상속이 불가하다. </p>
<hr>
<p>그럼 자바에서는 다중 상속을 아예 쓸 수 없을까 ?</p>
<h3 id="인터페이스">인터페이스</h3>
<p>자바의 인터페이스는 <strong>객체의 사용방법을 가이드라인 하는 것</strong>이다.</p>
<h4 id="특징">특징</h4>
<ul>
<li>다중 상속 가능
  인터페이스는 껍데기만 존재하기에 클래스 상속시 발생한 모호함이 없다.</li>
<li>추상 메소드와 상수만 사용 가능
  <del>인터페이스에는 구현 소스를 생성할 수 없다.</del>
  (자바 8부터는 디폴트 메소드 개념으로 구현 내용도 포함시킬 수 있다.)</li>
<li>생성자 사용 불가</li>
<li>메소드 오버라이딩 필수</li>
<li>가독성과 유지보수, 확장에는 열려있고 변경에는 닫혀있다.</li>
</ul>
<p>코드에서 먼저 인터페이스의 메소드를 호출하면 인터페이스는 객체의 메소드를 호출시킨다.</p>
<p>인터페이스끼리 상속을 통해서 확장이 가능하다. 
인터페이스로 객체 구현시 implements라는 키워드를 사용, 인터페이스끼리 상속 시 extends 키워드를 사용한다.</p>
<p>실제 예시는 현재 프로젝트에 인터페이스를 통한 상속을 하고 있어 추후에 정리할 예정이다..!</p>
<h3 id="정리">정리</h3>
<p>상속은 확장, 조상의 멤버를 그대로 자식이 물려받아 추가한다. 즉 자식으로 내려갈수록 조상보다 멤버의 범위가 더 넓어지는 느낌이다. 인터페이스는 확장의 의미보다는 인터페이스에서 정의된 것을 클래스에서 대신 구현하겠다는 느낌으로 받아들이면 될 것 같다.</p>
<hr>
<p>출처 : <a href="https://coding-factory.tistory.com/865">코드 팩토리 상속</a>
    <a href="https://coding-factory.tistory.com/867">코드 팩토리 인터페이스</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] 접근 제한자와 static/final정리]]></title>
            <link>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-%EC%A0%91%EA%B7%BC-%EC%A0%9C%ED%95%9C%EC%9E%90%EC%99%80-staticfinal%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-%EC%A0%91%EA%B7%BC-%EC%A0%9C%ED%95%9C%EC%9E%90%EC%99%80-staticfinal%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Sun, 06 Aug 2023 14:48:59 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 하면서 public과 private은 확실히 알지만 protected는 써보지 않았고, service단에서 repository를 항상 private final로 불러왔지만 &#39;왜&#39;인지는 알지못해 정리한다.</p>
<h3 id="접근-제한자">접근 제한자</h3>
<p>접근 제한자는 public, protected, private와 같이 세 가지 종류가 있다.</p>
<ul>
<li>public : 외부 클래스가 자유롭게 사용</li>
<li>protected : 같은 패키지 또는 자식 클래스</li>
<li>private : 외부에서 사용 불가능</li>
</ul>
<p>위 세 가지 접근 제한자가 적용되지 않으면 default 접근 제한을 가진다.</p>
<ul>
<li>default : 같은 패키지에 소속된 클래스에서만 사용할 수 있도록 한다.</li>
</ul>
<hr>
<h3 id="클래스-접근-제한">클래스 접근 제한</h3>
<p>클래스에서는 public과 default만 사용할 수 있다. default로 하면 해당 클래스를 같은 패키지내에서만 사용한다는 것이고, public으로 하면 다른 패키지도 허용하는 것이다.</p>
<hr>
<h3 id="생성자-접근-제한">생성자 접근 제한</h3>
<p>생성자는 다음과 같이 4가지 접근 제한을 모두 가질 수 있다.</p>
<pre><code class="language-java">public class ClassName {
    //public 
    public ClassName() {...}
    //protected
    protected ClassName() {...}
    //default
    ClassName() {...}
    //private
    private ClassName() {...}</code></pre>
<p>클래스에 생성자를 선언하지 않을 경우 컴파일러에 의해 자동으로 기본 생성자가 추가된다. 이때 기본 생성자의 접근 제한은 클래스의 접근 제한과 동일하다. 
protected 는 같은 패키지에 속하는 클래스에서 생성자 호출이 가능하다. 그리고 다른 패키지여도 해당 클래스의 자식 클래스이면 호출이 가능하다.</p>
<p>필드와 메소드 접근 제한 또한 생성자 접근 제한과 동일하다.</p>
<hr>
<h3 id="static">static</h3>
<p>클래스가 생성되지 않으면 함수를 사용할 수 없다. 하지만 꼭 함수가 클래스의 멤버 변수를 이용해 사용하는 것이 아니라 단순 계산 즉, 인스턴스와 관계없는 함수도 있을 수 있다. 이때 static을 사용한다. 
클래스가 로딩될 때 static 선언된 자원들은 JVM에서 메모리에 딱 한번 올라간다.
main또한 프로그램 실행시 인스턴스를 생성하지 않고 바로 실행되기에 static이 붙는다.</p>
<ul>
<li>장점<ul>
<li>공용 자원으로 쓰여 메모리를 아낄 수 있다.</li>
<li>속도가 빠르다.(객체 생성 시간이 없기 때문에)</li>
</ul>
</li>
<li>단점<ul>
<li>미리 메모리를 할당 받는 것이기에 무분별한 사용은 오히려 낭비가 될 수 있다.</li>
<li>static method에서는 static 변수만 사용이 가능하다.</li>
</ul>
</li>
</ul>
<p>가독성을 위해 import문에도 쓸 수 있다. 하지만 와일드 카드를 사용하는 것은 별로 권장되지 않는다고 한다.
(공식 가이드 출처)</p>
<h3 id="final">final</h3>
<p>final은 오직 한 번만 할당할 수 있는 entity를 정의할 때 사용한다. final로 선언된 변수는 항상 같은 값을 가진다. 
프로젝트에서 보면 MemberRepository 타입의 memberRepository 객체를 다른 객체로 바꾸지 않기 위해 final을 붙였던 것이다.
객체에 final을 붙이면 객체가 불변한다는 뜻으로 잘못 이해할 수 있는데 인스턴스 자체를 바꾸는 것이 불가능한 것이였고 인스턴스 내부의 값은 변경이 가능하다.</p>
<p>만약 값이 고정적이고 객체마다도 모두 동일한 값이 있다면 static final을 사용해 더 효율적으로 사용이 가능하다.</p>
<hr>
<p>출처 :
<a href="https://hongong.hanbit.co.kr/%EC%9E%90%EB%B0%94-%EC%A0%91%EA%B7%BC-%EC%A0%9C%ED%95%9C%EC%9E%90%EC%9D%98-%EC%A2%85%EB%A5%98%EC%99%80-%EC%B0%A8%EC%9D%B4public-protected-private/">혼공</a>
<a href="https://nowonbun.tistory.com/302">명월 일지 블로그</a>
<a href="https://itkjspo56.tistory.com/147">로춘남 티스토리</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] 자바 버전 별 정리]]></title>
            <link>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-%EC%9E%90%EB%B0%94-%EB%B2%84%EC%A0%84-%EB%B3%84-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-%EC%9E%90%EB%B0%94-%EB%B2%84%EC%A0%84-%EB%B3%84-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Sun, 06 Aug 2023 09:15:20 GMT</pubDate>
            <description><![CDATA[<p>자바8 과 자바17에는 큰 차이가 있다고 얼핏 들어왔다. 이번에는 확실히 정리해 차이를 알고자 한다. <img src="https://velog.velcdn.com/images/gourd_erased/post/83646b56-ce34-4234-9fa5-2c0798166d7e/image.png" alt=""></p>
<h3 id="se-ee-fx">SE? EE? FX???</h3>
<p>먼저 Java 뒤에 붙는 영어부터 알아보자.</p>
<ul>
<li>SE : Standard Edition의 약어이다. <strong>자바 표준 에디션</strong>은 가장 기본이 되는 에디션이다. 흔히 자바 언어라고 하는 대부분의 패키지가 포함된 에디션이다.</li>
<li>EE : Enterprise Edition은 Enterprise Edition으로 SE기반 위에 탑재된다. JSP, Servlet, JDBC를 포함한다.</li>
<li>FX : 인터넷 어플리케이션을 만들 때 사용된다고 한다.</li>
<li>ME : 작은 장치에서 동작하는 모바일용이다.</li>
</ul>
<p>일반적인 개발에서는 SE만 신경쓰면 될 것 같다!</p>
<hr>
<h3 id="lts">LTS</h3>
<p><strong>Long Term Support</strong>는 출시 후 8년이라는 긴 기간을 보안 업데이트와 버그 수정을 지원하는 버전이다.</p>
<pre><code> Oracle 인수 후 java8에서 등장한 lambda와 Stream등 편리한 기능으로 
 java 8에 대한 지원을 2030년 까지로 확장하기로 했다. </code></pre><p>❗ 따라서 왜 해당 버전을 사용하는 지 알기 위해서는 LTS인 8, 11, 17의 차이를 알면 될 것 같다.</p>
<hr>
<h3 id="버전-별-차이">버전 별 차이</h3>
<h4 id="자바-8">자바 8</h4>
<ol>
<li>Lambda</li>
<li>Stream</li>
<li>Interface default Method</li>
<li>Optional</li>
<li>LocalDateTime</li>
</ol>
<ul>
<li><p>Lambda
함수를 하나의 식으로 표현한 것이다. Java 8 이전 익명 클래스의 사용을 더욱 간결하게 바꾼 것이다.</p>
<pre><code class="language-java">List&lt;Integer&gt; intList = new ArrayList&lt;Integer&gt;();
intList.add(0);
intList.add(1);
intList.add(2);
/// 인스턴스의 메소드
List&lt;Integer&gt; resultList = intList.stream().map(intList::get).collect(Collectors.toList());
/// 클래스의 생성자
List&lt;Double&gt; resultList = intList.stream().map(Double::new).collect(Collectors.toList());
System.out.println(intList);
System.out.println(resultList);
 --------------------------------------------------------------------------------------
 람다와 메소드 레퍼런스로 단축 표현
 (String s) -&gt; System.out.println(s)  :       System.out::println
 (str, i) -&gt; str.substring(i)         :       String::substring

</code></pre>
</li>
</ul>
<ul>
<li><p>Stream</p>
<p>순차/병렬 작업을 지원한다. 하나의 문장으로 다양한 처리 기능을 구현이 가능하다.</p>
</li>
<li><p>Interface의 Default Methods
  인터페이스는 메소드 정의만 할 수 있고 구현은 할 수 없었지만, 디폴트 메소드라는 개념이 생겨 구현 내용도 인터페이스에 포함시킬 수 있다. 호환성 때문에 사용한다고 하는데 더 알아봐야 할 것 같다.</p>
<pre><code class="language-java">  public interface Calculator {
  public int plus(int i, int j);
  public int multiple(int i, int j);
  default int exec(int i, int j){      //default로 선언함으로 메소드를 구현할 수 있다.
      return i + j;
      }
  }

  //상속받은 클래스에서 구현
  public class MyCalculator implements Calculator {

      @Override
      public int plus(int i, int j) {
          return i + j;
      }
  }
     //상속받은 클래스에서 따로 구현 X
  public class MyCalculatorExam {
      public static void main(String[] args) {
          Calculator cal = new MyCalculator();
          int value = cal.exec(5,10);
          System.out.println(value);
         }
  }</code></pre>
</li>
<li><p>Optional</p>
<p>  Optional 구조체로 인해 간편하게 NPE(Null Pointer Exception) 이슈에 대응할 수 있다.
  try catch문을 줄일 수 있다. Optional도 사용법을 정리할 예정이다.</p>
</li>
<li><p>LocalDateTime
  기존 Date와 Calendar 클래스의 기능 부족과 비 표준 명명 규칙을 해결하기 위해 나온 API이다. </p>
</li>
</ul>
<hr>
<h4 id="자바-11">자바 11</h4>
<p>자바 11의 경우 개발자의 관점에서는 큰 차이는 없다. </p>
<ol>
<li>Lambda 매개변수에 대한 지역 변수 유형 추론</li>
<li>String 메소드 추가</li>
</ol>
<p>string의 공백 관련 메소드가 추가된 것과 람다 표현식에 var사용 가능한 것을 제외하고는 8과 큰 차이가 없는 것처럼 느껴졌다.</p>
<hr>
<h4 id="자바-17">자바 17</h4>
<ol>
<li>Pattern Matching fot switch</li>
<li>Sealed Classes</li>
</ol>
<ul>
<li><p>Pattern Matching fot switch
  코드 가독성 향상과 불필요한 캐스팅, 조건문을 줄일 수 있게 되었다.</p>
<pre><code class="language-java">  static void typeTester(Object o) {
      switch(o) {
          case null -&gt; System.out.println(&quot;null&quot;);
          case String s -&gt; System.out.println(&quot;String&quot;);
      }
  }
</code></pre>
<p>이외에도 enum형이나 다른 자료형도 받을 수 있다.</p>
</li>
<li><p>Sealed Classes
  상속하거나 구현할 클래스를 지정해두고, 해당 클래스들만 상속/구현이 가능하도록 제한하는 기능이다. 이를 통해 어떤 클래스가 해당 클래스를 상속받는지를 쉽게 알 수 있고 제한할 수 있다.</p>
<p>  게임 예시를 들면 Character라는 class에서 주인공과 몬스터를 두고 
  주인공인 Hero클래스에서 permit을 젤다 클래스에만 주는 것이다. 그러면 Monster클래스는 절대 상속을 할 수 없다.</p>
</li>
</ul>
<hr>
<p>참고
<a href="https://velog.io/@ililil9482/Java17%EC%9D%84-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C">https://velog.io/@ililil9482/Java17%EC%9D%84-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C</a></p>
<p><a href="https://projooni.tistory.com/entry/Java-%EB%B2%84%EC%A0%84%EB%B3%84-%EC%B0%A8%EC%9D%B4%EC%99%80-%ED%8A%B9%EC%A7%95">https://projooni.tistory.com/entry/Java-%EB%B2%84%EC%A0%84%EB%B3%84-%EC%B0%A8%EC%9D%B4%EC%99%80-%ED%8A%B9%EC%A7%95</a></p>
<p><a href="https://hajoung56.tistory.com/35">https://hajoung56.tistory.com/35</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] JAVA버전과 JDK 버전]]></title>
            <link>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-JDK%EA%B0%80-%EB%AD%90%EC%95%BC</link>
            <guid>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-JDK%EA%B0%80-%EB%AD%90%EC%95%BC</guid>
            <pubDate>Sat, 05 Aug 2023 14:38:44 GMT</pubDate>
            <description><![CDATA[<h3 id="람다와-스트림에서">람다와 스트림에서?</h3>
<p>람다와 스트림을 공부해보면 자바8 이상부터라는 말을 볼 수 있다. 하지만 java버전을 보면 jre, jdk 1.8과 8 등 자바 8이 무엇을 말하는지 헷갈리는 부분들이 많아 정리한다. </p>
<h3 id="먼저-jvm-jre-jdk-부터">먼저 JVM, JRE, JDK 부터!</h3>
<ul>
<li><p>JVM
<img src="https://velog.velcdn.com/images/gourd_erased/post/49bc02a7-e3f9-4814-b96b-d30224c0aab8/image.png" alt=""></p>
<ul>
<li><p>java virtual machine</p>
<p>자바 가상머신으로 자바 프로그램 실행환경을 만들어준다. 자바 코드를 컴파일하면 .class 바이트 코드로 만들어지는데 이를 JVM에서 실행하는 것이다. JVM은 밑에 나오는 JRE에 포함되어있다.</p>
<p>따라서 운영체제에 맞는 JRE가 설치되어있다면 JVM은 설치되어있는 것이다.</p>
<p>   .class 파일은 바이트 코드로 기계어로 가는 단계의 중간이라고 생각하면 된다.</p>
<ul>
<li><p>바이트 코드를 읽는 방식
바이트 코드를 한 줄씩 해석하는 <strong>Interpreter</strong>방식과 <strong>JIT</strong>컴파일 방식 두 가지를 혼용한다. Interpreter는 느리고 JIT는 빠른 대신 비용 소모가 있다. 따라서 JVM은 인터프리터를 사용하다가 일정 기준을 넘기면 JIT 컴파일 방식을 사용한다.</p>
<p>   JIT(Just In Time) 컴파일러란 바이트 코드를 실행하는 시점에 각 OS에 맞는  Native Code로 변환하여 실행 속도를 개선한다. 
 컴파일 시 해당 코드를 캐싱한다. 이후 바뀐 부분만 컴파일을 하기 때문에 성능이 좋다.</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<ul>
<li>JRE<ul>
<li>Java Runtime Environment의 약어다.</li>
<li>JVM을 동작하는데 필요한 각종 라이브러리를 담고있다.</li>
<li>만약 단순 자바 프로그램을 실행하는 것이면 JRE만 다운받아도 된다.</li>
</ul>
</li>
</ul>
<ul>
<li>JDK<ul>
<li>Java Development Kit의 약자어다.</li>
<li>JDK = JRE + development tools</li>
<li>JRE 외에 개발을 위해 필요한 도구를 갖고 있는 것이다.</li>
<li>javac(컴파일러)와 javadocs(자바 문서 생성기) 및 jdb(자바 디버거)와 같은 도구들이 포함되어있다.</li>
</ul>
</li>
</ul>
<h3 id="그래서-자바-8은-뭐야">그래서 자바 8은 뭐야??</h3>
<p>자바 몇 버전이라는 언어 규격이 있고 그 규격을 구현한 것을 JDK로 보면 편하다.
위에서 봤듯이 <code>JDK</code>에는 javac가 있기 때문에 <code>JDK17</code>이라하면 javac 버전도 <code>자바17</code>에 맞추어진다.</p>
<p>따라서 자바8을 사용하기 위해서는 <code>JDK1.8</code>이나 1.8 위 버전을 사용해야한다.
<code>Java 8</code>까지는 해당 버전에 맞는 JDK는 <code>JDK1.8</code>이라 부르고  <code>JAVA 9</code>이상은 <code>JDK 9</code>로 쓴다.</p>
<p>결국 완전히 같지는 않지만 자바 버전과 JDK의 버전을 똑같다 생각해도 좋을 것 같다.</p>
<p>출처:
<a href="https://coding-factory.tistory.com/827">https://coding-factory.tistory.com/827</a>
<a href="https://highright96.tistory.com/70">https://highright96.tistory.com/70</a>
<a href="https://okky.kr/questions/587890">https://okky.kr/questions/587890</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JPA] 자바 ORM 표준 JPA 프로그래밍 ch.6 ]]></title>
            <link>https://velog.io/@gourd_erased/JPA-%EC%9E%90%EB%B0%94-ORM-%ED%91%9C%EC%A4%80-JPA-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-ch.6</link>
            <guid>https://velog.io/@gourd_erased/JPA-%EC%9E%90%EB%B0%94-ORM-%ED%91%9C%EC%A4%80-JPA-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-ch.6</guid>
            <pubDate>Tue, 01 Aug 2023 05:55:54 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/gourd_erased/post/1d2a4084-ab0b-4fe3-a022-03d1a017e072/image.png" alt=""></p>
<blockquote>
<p>김영한님의 &#39;<strong>자바 ORM 표준 JPA 프로그래밍</strong>&#39;을 읽고 정리한 글입니다.</p>
</blockquote>
<hr>
<h1 id="6장">6장</h1>
<h3 id="✔--다대일">✔  다대일</h3>
<ul>
<li>객체 양방향 관계에서 연관관계의 주인은 항상 다쪽이다.</li>
<li>양방향은 외래 키가 있는 쪽이 주인이다.
  예시에서 Team.members느 조회를 위한 JPQL이나 객체 그래프를 탐색할 때만 사용한다.</li>
<li>양방향은 항상 서로를 참조해야 한다.
  어느 한 쪽만 참조하면 연관관계가 성립하지 않는다.
  예시처럼 setTeam()이나 addMember()로 메소드를 작성하는 것이 좋다.</li>
</ul>
<hr>
<h3 id="✔-일대다">✔ 일대다</h3>
<ul>
<li>다대일 관계의 반대 방향이다. 항상 하나 이상 참조할 수 있으므로 자바 컬렉션인 Collection, List, Set, Map 중에 하나를 사용해야 한다.</li>
</ul>
<h4 id="일대다-단방향">일대다 단방향</h4>
<p>먼저 일대다 단방향은 잘 사용하지 않는 것이 좋다. 
왜 그런지 살펴보자.</p>
<pre><code class="language-java">public class Team {
    @ID @GeneratedValue
    @Column(name = &quot;TEAM_ID&quot;)
    private Long id;

      ...

    @OneToMany
    @JoinColumn(name =&quot;TEAM_ID&quot;)
    private List&lt;Member&gt; members = new ArrayList&lt;Member&gt;();</code></pre>
<p>일대다 단방향시 @JoinColumn 명시를 하지 않을 경우 JPA는 <code>조인 테이블</code>전략을 사용해 매핑한다.</p>
<ul>
<li><p>단점</p>
<ul>
<li><p>매핑한 객체가 관리하는 외래 키가 다른 테이블에 있다.</p>
<pre><code class="language-java">public void testSave() {
  Member member = new Member(&quot;member1&quot;);

  Team team = new Team(&quot;team1&quot;);
  team.getMembers().add(member);

  em.persist(member);
  em.persist(team);

  transaction.commit();
}</code></pre>
<p>이 경우 쿼리문을 보면 insert문 두번에 추가로 update문이 나간다!!
연관관계에 대한 정보는 Team의 members가 관리하기 때문이다. Member엔티티를 저장시 MEMBER테이블의 TEAM_ID 외래키에는 아무 값도 저장되지 않는다. 대신 Team엔티티를 저장할 때 Team.members의 참조 값을 확인해 업데이트하는 것이다.</p>
</li>
</ul>
</li>
</ul>
<p>-&gt; 성능, 관리도 힘들다 때문에 다대일 <strong>양방향을 권장!</strong></p>
<h4 id="일대다-양방향">일대다 양방향</h4>
<p>일대다 단방향 + 다대일 단방향을 추가한 것이다. 이렇게 되면 둘 다 같은 키를 관리한다. 따라서
반대편 다대일을 insertable=false, updatable=false로 설정해 읽기만 가능하게 해야한다.
이는 일대다 단방향의 단점이 그대로 있으므로 <strong>다대일 양항뱡 권장!</strong></p>
<hr>
<h3 id="✔-일대일">✔ 일대일</h3>
<p>양쪽이 서로 하나의 관계만 가진다. 회원과 사물함의 관계와 같다.
일대일은 다가 없으므로 어디든 키를 관리할 수 있다. 선택이 필요하다.</p>
<h4 id="주-테이블에-외래-키">주 테이블에 외래 키</h4>
<p>JPA는 주 테이블에 외래 키가 있으면 좀 더 편리하게 매핑할 수 있다.</p>
<h4 id="대상-테이블에-외래-키">대상 테이블에 외래 키</h4>
<p>JPA는 일대일 관계에서 대상 테이블에 외래 키가 있는 단방향 관계는 지원하지 않는다.</p>
<pre><code>프록시 사용시 외래 키를 직접 관리하지 않는 일대일 관계는 지연 로딩으로 설정해도 즉시 로딩된다. 
예제에서 Locker.member는 지연로딩이 가능하지만 Member.locker는 즉시 로딩된다.
이것은 프록시의 한계때문이다.(8장)</code></pre><hr>
<h3 id="✔-다대다">✔ 다대다</h3>
<p>회원과 상품의 관계는 다대다 관계이다. 중간 연결 테이블이 필요하다. Member_Product 연결 테이블이 그것이다. 그런데 객체는 테이블과 다르게 객체 2개로 다대다 관계를 만들 수 있다. 회원 객체는 컬렉션을 사용해서 상품들을 참조하면 되고 상품들도 컬렉션을 사용해서 회원들을 참조하면 된다. </p>
<p><code>@ManyToMany</code>를 사용하면 편리하게 매핑이 가능하다.</p>
<ul>
<li>한계<ul>
<li><strong>편리해보이지만 실무에서 사용하지 않는다.</strong></li>
<li>연결 테이블이 단순히 연결만 하는 것이 아니기 때문</li>
<li><blockquote>
<p>회원 - 상품에서 주문시간, 수량 같은 데이터가 들어올 수도 있다.</p>
</blockquote>
</li>
</ul>
</li>
</ul>
<h4 id="한계-극복">한계 극복</h4>
<pre><code>- 연결 테이블용 엔티티를 추가해야한다. 
- @ManyToMany를 @OneToMany, @ManyToOne으로 풀어야한다.</code></pre><p><img src="blob:https://velog.io/9fa29fb7-8b35-4d72-bcf1-4ab9c1d9728b" alt="업로드중.."></p>
<hr>
<blockquote>
<h3 id="✔--정리">✔  정리</h3>
</blockquote>
<ul>
<li>테이블의 N:M 관계는 중간 테이블을 이용해서 1:N, N:1로 만들어야한다.</li>
<li>실무에서 중간테이블이 단순하지 않고 @ManyToMany는 제약이 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JPA] 자바 ORM 표준 JPA 프로그래밍 ch.5 ]]></title>
            <link>https://velog.io/@gourd_erased/JPA-%EC%9E%90%EB%B0%94-ORM-%ED%91%9C%EC%A4%80-JPA-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-ch.5</link>
            <guid>https://velog.io/@gourd_erased/JPA-%EC%9E%90%EB%B0%94-ORM-%ED%91%9C%EC%A4%80-JPA-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-ch.5</guid>
            <pubDate>Sun, 30 Jul 2023 06:26:52 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/gourd_erased/post/1d2a4084-ab0b-4fe3-a022-03d1a017e072/image.png" alt=""></p>
<blockquote>
<p>김영한님의 &#39;<strong>자바 ORM 표준 JPA 프로그래밍</strong>&#39;을 읽고 정리한 글입니다.</p>
</blockquote>
<hr>
<h1 id="5장">5장</h1>
<h3 id="✔--단방향-연관관계">✔  단방향 연관관계</h3>
<p>회원과 팀의 관계를 통해 이해해야한다</p>
<ul>
<li>객체 기준
회원 객체는 Member.team으로 연관을 맺는다.
<code>단방향</code>이므로 team-&gt; member는 얻을 수 없다.</li>
<li>테이블 기준
회원 테이블과 팀 테이블은 TEAM_ID로 연관관계를 맺는다. 조인을 할 수 있으므로 <code>양방향</code> 관계이다.<h4 id="manytoone">ManyToOne</h4>
</li>
</ul>
<p>JPA를 사용하여 위 둘을 매핑해보자.</p>
<pre><code class="language-java">@Entity
public class Member {
    ...
    @ManyToOne
    @JoinColumn(name=&quot;TEAM_ID&quot;)
    private Team team;
</code></pre>
<p>여기서 <code>@JoinColumn</code>은 외래 키를 매핑할 때 사용한다. 생략이 가능하다.
만약 생략시 기본 전략을 이용한다.</p>
<pre><code>필드명 + _ + 참조하는 테이블의 컬럼명</code></pre><p>@ManyToOne 속성</p>
<ul>
<li>optional : 기본 값 true,  false시 연관 엔티티가 항상 있어야한다.</li>
<li>fetch : 글로벌 페치 전략 뒤에 나옴 (8장)</li>
<li>cascade : 영속성 전이 기능을 사용 (8장)</li>
<li>targetEntity : 연관된 엔티티 타입 정보 설정, 거의 사용 X</li>
</ul>
<hr>
<h3 id="✔-양방향-연관관계">✔ 양방향 연관관계</h3>
<p>반대로 팀에서 회원으로 접근하는 관계를 추가하자.</p>
<ul>
<li>회원 -&gt; 팀(Member.team)</li>
<li>팀 -&gt; 회원(Team.members)</li>
</ul>
<p>Team 엔티티</p>
<pre><code class="language-java">@OneToMany(mappedBy = &quot;team&quot;)
private List&lt;Member&gt; members = new ArrayList&lt;Member&gt;();</code></pre>
<hr>
<h3 id="✔-연관관계의-주인">✔ 연관관계의 주인</h3>
<h4 id="mappedby-속성은-왜-필요할까">mappedBy 속성은 왜 필요할까?</h4>
<p> 객체에는 양방향 연관관계라는 것이 없다.
 서로 다른 단방향 연관간계를 묶은 것이다.</p>
<p> 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리한다.
 엔티티를 단방향으로 매핑하면 참조를 하나만 사용한다.
 하지만 양방향 매핑이면 <strong>객체의 참조</strong>는 <strong>둘</strong> 인데 <strong>외래 키</strong> 는 <strong>하나</strong> 이다.</p>
<p> -&gt; JPA에서는 두 연관관계 중 하나를 정해 테이블의 외래키를 관리한다. 이를 <code>연관관계의 주인</code>이라고 한다.</p>
<h4 id="규칙">규칙</h4>
<p> 연관관계의 주인만이 외래 키를 관리(등록,수정,삭제)할 수 있다.
 주인이 아닌 쪽은 읽기만 가능하다.</p>
<ul>
<li><p>주인은 mappedBy 속성을 사용하지 않는다.</p>
</li>
<li><p>주인이아니면 mappedBy속성을 사용해 주인을 지정해야한다.</p>
</li>
<li><p><em>연관관계의 주인을 정하는 것은 <code>외래키 관리자</code>를 정하는 것이다.*</em>
만약 Member.team을 주인으로 선택하면 자기 테이블에 있는 외래 키를 관리하면 된다.
하지만 Team.member를 선택하면 물리적으로 다른 테이블의 외래 키를 관리해야한다.</p>
<h4 id="연관관계의-주인은-외래-키가-있는-곳">연관관계의 주인은 외래 키가 있는 곳</h4>
<p>여기서는 회원 테이블이 외래 키를 갖고 있으므로 Member.team이 주인이 된다.
Team.members에는 (mappedBy=&quot;team&quot;)속성을 사용해야한다. 여기서team은 Member엔티티의 team 필드이다.</p>
</li>
</ul>
<pre><code>데이터 베이스의 다대일, 일대다 관계는 항상 `다`쪽이 외래 키를 가진다.  따라서 
`다`쪽인 @ManyToOne은 항상 연관관계의 주인이므로 mappedBy를 설정할 수 없다.</code></pre><hr>
<h3 id="✔-양방향-연관관계-저장">✔ 양방향 연관관계 저장</h3>
<p>저장 방식은 단방향 방식과 동일하다.
엔티티 매니저는 주인을 통해서 외래 키를 관리한다.</p>
<pre><code class="language-java">team1.getMembers().add(member1) // 무시된다.(연관관계의 주인이 아니기에!!)</code></pre>
<p>team이 아닌 member 엔티티를 통해서 해야한다.</p>
<pre><code class="language-java">member1.setTeam(team1) //연관관계 설정</code></pre>
<hr>
<h3 id="✔-양방향-연관관계의-주의점">✔ 양방향 연관관계의 주의점</h3>
<p>흔히 하는 실수는 연관관계의 주인에는 값을 입력하지 않고 주인이 아닌 곳에만 값을 입력하는 것이다. </p>
<pre><code class="language-java">//회원 1,2 를 저장 후
Team team1 = new Team(&quot;team1&quot;, &quot;팀1&quot;);
//주인이 아닌 곳에만 연관관계 설정
team1.getMembers().add(member1);
team2.getMembers().add(member2);

em.persist(team1);</code></pre>
<p>위에서 봤듯이 Member.team에는 null들어가게 된다.</p>
<h4 id="순수한-객체까지-고려한-양방향-연관관계">순수한 객체까지 고려한 양방향 연관관계</h4>
<p>그럼 주인에만 저장해도 될까? -&gt; 객체 관점으로는 양쪽 방향 모두 값을 넣어주는 것이 안전하다.
<strong>JPA를 사용하지 않은 순수한 객체 코드에서는 null이 들어간다.</strong>
결론은 양방향 연관관계는 양쪽 모두 관계를 맺어주어야한다.</p>
<hr>
<blockquote>
<h3 id="✔--정리">✔  정리</h3>
</blockquote>
<ul>
<li>양방향의 장점은 반대 방향으로 객체 그래프 탐색 기능이 추가된 것.</li>
<li>연관관계의 주인은 외래 키의 위치와 관련해서 정해야한다.(중요도로 접근 X)</li>
<li>양방향 매핑 시 무한루프에 빠지지 않게 조심하자.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] Long과 long의 차이(Wrapper class)]]></title>
            <link>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-Long%EA%B3%BC-long%EC%9D%98-%EC%B0%A8%EC%9D%B4Wrapper-class</link>
            <guid>https://velog.io/@gourd_erased/%EC%9E%90%EB%B0%94-Long%EA%B3%BC-long%EC%9D%98-%EC%B0%A8%EC%9D%B4Wrapper-class</guid>
            <pubDate>Sun, 16 Jul 2023 04:52:34 GMT</pubDate>
            <description><![CDATA[<p>진행하고 있는 프로젝트에서 거의 다 Long을 쓰기에 wrapper class에 대해 정리해봤다. </p>
<p>먼저 차이를 알기 위해서는 원시 타입과 참조 타입을 알아야 한다.</p>
<h2 id="primitive-type이란">Primitive Type이란</h2>
<p>가끔 에러에서 보이면 뭔가 했는데 말 그대로 기본 타입으로 8가지가 있다. 크게 정수형, 실수형, 문자형, 논리형이 있다.</p>
<p>실제 메모리에 데이터 값을 직접 저장하는 타입이다.</p>
<ul>
<li>정수형 : byte, short, int, long</li>
<li>실수형 : float, double</li>
<li>논리형 : boolean</li>
<li>문자형 : char</li>
</ul>
<h2 id="reference-type이란">Reference Type이란</h2>
<p>참조 타입은 메모리 주소 값을 통해 객체를 참조하는 타입이다.</p>
<p>문자열, 배열, enum, 클래스 등이 있다. </p>
<p>참조 타입은 힙 영역의 객체를 참조하지 않는다는 뜻으로 null값을 가질 수 있다. 하지만 기본 타입은 null 지정이 불가능하다.</p>
<p>기본 타입을 참조 타입으로 변환시킬 때 Wrapper class를 사용한다.</p>
<h2 id="wrapper-class란">wrapper class란</h2>
<p>래퍼 클래스는 위 8가지의 기본 타입에 해당하는 데이터를 객체로 포장해주는 클래스이다.</p>
<table>
<thead>
<tr>
<th>기본 타입</th>
<th>래퍼 클래스</th>
</tr>
</thead>
<tbody><tr>
<td>byte</td>
<td>Byte</td>
</tr>
<tr>
<td>short</td>
<td>Short</td>
</tr>
<tr>
<td>int</td>
<td>Integer</td>
</tr>
<tr>
<td>long</td>
<td>Long</td>
</tr>
<tr>
<td>float</td>
<td>Float</td>
</tr>
<tr>
<td>double</td>
<td>Double</td>
</tr>
<tr>
<td>char</td>
<td>Character</td>
</tr>
<tr>
<td>boolean</td>
<td>Boolean</td>
</tr>
</tbody></table>
<h2 id="박싱과-언박싱">박싱과 언박싱</h2>
<p>박싱 : 기본 타입 → 래퍼 클래스</p>
<p>언박싱 : 래퍼 클래스 → 기본 타입</p>
<p>자바 컴파일러는 이를 자동으로 처리해준다.</p>
<pre><code class="language-java">Character ch = &#39;g&#39; // 오토 박싱
char c = ch; //오토 언박싱
sout(c); // g가 정상적으로 출력</code></pre>
<p>하지만 아래 경우는 조심해야한다.</p>
<pre><code class="language-java">Integer num1 = new Integer(7); // 박싱

      Integer num1 = new Integer(100); // 박싱
        Integer num2 = new Integer(100); // 박싱
            Integer num3 = new Integer(200); //박싱

            sou(num1 &lt; num3) // true
            sout(num1 == num2) // false
            sout(num1.equals(num2)) //true
    }</code></pre>
<p>래퍼 클래스의 비교연산은 오토 언박싱으로 알아서 100과 200을 비교해준다. 하지만 동등 여부 판단은 객체끼리의 비교가 되어 주소값을 비교한다. 때문에 값이 어떤 값이든 무조건 false가 나오게 된다!!</p>
<p>비교를 위해서는 equals() 메소드를 사용해야한다.</p>
<h2 id="그래서-long과-long의-차이는">그래서 Long과 long의 차이는?</h2>
<h3 id="속도와-메모리">속도와 메모리</h3>
<p>기본 타입은 직접 값을 할당해 ‘Stack’에 값이 존재하고 </p>
<p>참조 타입의 경우는 ‘Stack’에는 주소 정보만 있고 실제 데이터는 ‘Heap’에 있다. </p>
<p>따라서 값을 가져오는 속도나 메모리 둘 다 기본 타입이 낫다고 할 수 있다.</p>
<h3 id="다-long을-사용하는-이유는">다 Long을 사용하는 이유는?</h3>
<p>이유는 <strong>null을 사용할 수 있는지 여부</strong>에 있다.</p>
<p>기본 타입에서는 기본값이 0인데 그렇다면 request를 받았을 때 id값이 실제로 0인것인지 값이 없어서 0인지 구분하기 어렵다. </p>
<p>Wrapper 타입인 Long을 사용하면 id가 없는 경우에는 확실히 null이고, 그 자체로 id가 없다는 걸 보장할 수 있다.</p>
<hr>
<p>출처 :
<a href="http://www.tcpschool.com/java/java_api_wrapper">코딩교육 티씨피스쿨</a>
<a href="https://www.inflearn.com/questions/35759/long-%ED%83%80%EC%9E%85%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A7%88%EB%AC%B8%EC%9E%85%EB%8B%88%EB%8B%A4">Long 타입에 대한 질문입니다. - 인프런 | 질문 &amp; 답변</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] stream 사용하기]]></title>
            <link>https://velog.io/@gourd_erased/JAVA-stream-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@gourd_erased/JAVA-stream-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 10 Jul 2023 15:34:26 GMT</pubDate>
            <description><![CDATA[<h3 id="stream이란">stream이란?</h3>
<p>데이터 소스를 추상화하고, 데이터를 다루는데 자주 사용되는 메소드를 정의해놓은 것이다.
추상화하였기에 코드의 재사용성이 높아진다. 또한 데이터를 모두 같은 방식으로 다룰 수 있다.</p>
<ul>
<li>스트림은 데이터 소스를 변경하지 않는다.</li>
<li>스트림은 일회용이다.</li>
<li>스트림은 작업을 내부 반복으로 처리한다.</li>
</ul>
<h4 id="분류">분류</h4>
<ul>
<li>중간 연산 : 연산 결과가 스트림인 연산, 연속해서 중간 연산이 가능</li>
<li>최종 연산 : 결과가 스트림이 아닌 연산, 스트림의 요소를 소모하므로 단 한번만 가능</li>
</ul>
<p>중간 연산은 map()과 최종 연산은 reduce()와 collect()가 핵심이라고 한다.</p>
<h3 id="map">map</h3>
<p>스트림의 요소에 저장된 값 중에서 원하는 필드만 뽑아내거나 특정 형태로 변환한다.
다음 코드는 문자열 스트림을 각 문자열의 길이로 이루어진 스트림을 반환한다.</p>
<pre><code class="language-java">Stream&lt;String&gt; stream = Stream.of(&quot;abc&quot;, &quot;ab&quot;, &quot;abcd&quot;, &quot;abcde&quot;);
stream.map(s -&gt; s.length())
    .forEach(System.out::println);
   //출력결과
  3
  2
  4
  5</code></pre>
<h3 id="collect">collect()</h3>
<p>collect() 메소드는 Collectors 객체에 구현된 방법대로 스트림의 요소를 수집한다. 다양한 방법이 클래스 메소드로 정의되어 있다. toList만 확인하자.
최종 연산에 .collect로, Collector.toList()처럼 메소드를 사용하면 된다.</p>
<pre><code class="language-java">List&lt;WorryBoard&gt; worries = worryBoardRepository.findByState(false);
    List&lt;WorryResponse&gt;  worriesRes = worries
        .stream()
        .map(WorryResponse::new)
        .collect(Collectors.toList());</code></pre>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] 람다식과 메소드 참조]]></title>
            <link>https://velog.io/@gourd_erased/JAVA-%EB%9E%8C%EB%8B%A4%EC%8B%9D%EA%B3%BC-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%B0%B8%EC%A1%B0</link>
            <guid>https://velog.io/@gourd_erased/JAVA-%EB%9E%8C%EB%8B%A4%EC%8B%9D%EA%B3%BC-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%B0%B8%EC%A1%B0</guid>
            <pubDate>Mon, 10 Jul 2023 14:53:43 GMT</pubDate>
            <description><![CDATA[<p>처음 자바 코드를 보고 화살표는 뭔가 했었다. 스프링 프로젝트를 하며, 교재를 보면서도, 따라만 쳤기에 &quot;자바의 정석&quot;을 보고 일부분만 정리했다.</p>
<h3 id="람다식이란">람다식이란</h3>
<p>람다식은 메소드를 하나의 &#39;식&#39;으로 표현한 것이다.
메소드를 람다식으로 표현하면 이름과 반환값이 없어지므로 <code>익명함수</code>라고도 한다.
라다식은 메소드의 매개변수로 전달되어지는 것과 메소드의 결과로 반환될 수도 있다.
변수처럼 쓸 수 있는 것이다.</p>
<h3 id="작성하기">작성하기</h3>
<pre><code>반환타입 메소드이름(매개변수 선언) {
    문장들
}</code></pre><p>람다식은 이름과 반환타입을 제거하고 선언부와 몸통{} 사이에 <code>-&gt;</code>를 추가한다.</p>
<p><del>반환타입 메소드이름</del> (매개변수 선언) -&gt; {
}</p>
<pre><code>(매개변수 선언) -&gt; {
}</code></pre><p>예시로 </p>
<pre><code class="language-java">int max(int a, int b) {
    return a &gt; b ? a : b;
}</code></pre>
<p>위 코드에서 아래처럼 바꿀 수 있다.</p>
<pre><code class="language-java">(int a, int b) -&gt; { a &gt; b ? a : b} //식으로 반환시 ; 붙이지 않는다.</code></pre>
<ul>
<li>람다식에 선언된 매개변수의 타입은 추론이 가능한 경우 생략 가능하다.</li>
<li>매개변수와 타입이 있으면 괄호를 생략할 수 없다.</li>
<li>괄호 { } 안의 문장이 하나일 때는 괄호 생략이 가능하다.</li>
<li>안의 문장이 return문일 경우에는 괄호 생략이 불가능하다.</li>
</ul>
<hr>
<h3 id="메소드-참조">메소드 참조</h3>
<p>람다식이 하나의 메소드만 호출하는 경우에는 &#39;메소드 참조&#39;를 통해 간략히 할 수 있다.</p>
<pre><code class="language-java">//f의 타입만 봐도 두 개의 String 매개변수를 받는 다는 것을 알 수 있다.
function&lt;String, String, Boolean&gt; f = (s1, s2) -&gt; s1.equals(s2);
//추론 가능 매개변수 제거
function&lt;String, String, Boolean&gt; f = String::equals;</code></pre>
<h4 id="생성자의-메소드-참조">생성자의 메소드 참조</h4>
<pre><code class="language-java">Supplier&lt;MyClass&gt; s = () -&gt; new MyClass(); // 람다식
Supplier&lt;MyClass&gt; s = MyClass::new;           // 메소드 참조</code></pre>
<p>매개변수가 있는 생성자라면 매개변수의 개수에 따라 알맞은 함수형 인터페이스를 사용하면 된다.</p>
<pre><code class="language-java">Function&lt;Integer, MyClass&gt; f = (i) -&gt; new MyClass(i); // 람다식
Function&lt;Integer, MyClass&gt; f2 = MyClass::new           // 메소드 참조</code></pre>
]]></description>
        </item>
    </channel>
</rss>