<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hyeok_b2ng.log</title>
        <link>https://velog.io/</link>
        <description>반갑습니다 👋</description>
        <lastBuildDate>Wed, 17 Jul 2024 15:22:59 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>hyeok_b2ng.log</title>
            <url>https://velog.velcdn.com/images/hyeok_b2ng/profile/4879b178-c19d-4068-ac5a-d4c1f2ccafcb/image.JPG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. hyeok_b2ng.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hyeok_b2ng" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[JPA 기본편 - 매핑]]></title>
            <link>https://velog.io/@hyeok_b2ng/JPA-%EA%B8%B0%EB%B3%B8%ED%8E%B8-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EB%A7%A4%ED%95%91-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@hyeok_b2ng/JPA-%EA%B8%B0%EB%B3%B8%ED%8E%B8-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EB%A7%A4%ED%95%91-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Wed, 17 Jul 2024 15:22:59 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 게시글의 출처는 인프런 김영한님의 강의입니다.
강의명은 자바 ORM 표준 JPA 프로그래밍 - 기본편 입니다.
출처 : <a href="https://www.inflearn.com/course/ORM-JPA-Basic">https://www.inflearn.com/course/ORM-JPA-Basic</a></p>
</blockquote>
<h1 id="연관관계-매핑-기초">연관관계 매핑 기초</h1>
<h2 id="단방향-연관관계">단방향 연관관계</h2>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/70d236e2-2fef-422e-baab-d9d2ad54def8/image.png" alt="">
<code>@JoinColumn(name = &quot;TEAM_ID&quot;)</code>
이런 식으로 객체를 필드로 선언하고 매핑할 컬럼을 지정해준다.</p>
<h2 id="양방향-연관관계와-연관관계의-주인">양방향 연관관계와 연관관계의 주인</h2>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/30afeb8f-b057-4614-899e-f6fc1d304576/image.png" alt=""></p>
<ul>
<li>단방향과 전혀 차이가 없음</li>
<li>테이블에 개념에서 보면 방향이라는 개념이 없다. why? 외래키 하나로 조인을 통해 양방향으로 이동이 모두 가능하기 때문 !<pre><code class="language-java">@OneToMany(mappedBy = &quot;team&quot;)
private List&lt;Member&gt; members = new ArrayList&lt;&gt;();</code></pre>
이렇게 member리스트를 Team에 추가해서 객체관적에서 양방향 매핑이 가능하다.</li>
</ul>
<h3 id="연관관계의-주인과-mappedby">연관관계의 주인과 mappedBy</h3>
<p>매우 중요하다.</p>
<ul>
<li>mappedBy는 처음에는 이해하기 어렵다.</li>
<li>객체와 테이블간의 연관관계를 맺는 차이를 이해해야 한다.</li>
</ul>
<p><strong>객체와 테이블이 관계를 맺는 차이</strong></p>
<ul>
<li>객체 연관관계 = 2개<ul>
<li>회원 -&gt; 팀 연관관계 1개 (단방향)</li>
<li>팀 -&gt; 회원 연관관계 1개 (단방향)</li>
</ul>
</li>
<li>테이블 연관관계 = 1개<ul>
<li>회원 &lt;-&gt; 팀의 연관관계 1개 (양방향)</li>
</ul>
</li>
</ul>
<p>즉, 객체의 양방향 관계는 사실 양방향관계가 아니라 서로 다른 단방향 관계 2개이다.</p>
<h3 id="테이블의-양방향-연관관계">테이블의 양방향 연관관계</h3>
<ul>
<li>테이블은 외래 키 하나로 두 테이블의 연관관계를 관리</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/7487024e-d8ee-465b-90f9-09f5f9043857/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/2eee1f34-cf98-45c4-ab2b-31709358ecc1/image.png" alt=""></p>
<p>멤버를 새로운 팀에 넣고 싶다. -&gt; 과연 멤버의 팀 값을 바꿔야할지 ? 팀의 members의 값을 바꿔야할 지 ?
둘 중 하나로 외래 키를 관리해야한다 !</p>
<h3 id="연관관계의-주인-owner">연관관계의 주인 (Owner)</h3>
<p>양방향 매핑 규칙</p>
<ul>
<li>객체의 두 관계 중 하나를 연관관계의 주인으로 지정</li>
<li>연관관계의 주인만이 왜래 키를 관리 (등록, 수정)</li>
<li>주인이 아닌 쪽은 읽기만 가능</li>
<li>주인은 mappedBy 속성 사용 X</li>
<li>주인이 아니면 mappedBy 속성으로 주인 지정</li>
</ul>
<h4 id="누구를-주인으로-">누구를 주인으로 ?</h4>
<ul>
<li>외래 키가 있는 곳을 주인으로 정해라</li>
<li>여기서는 Member.team이 연관관계의 주인</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/766c5130-e6ed-403d-be7a-890ca3292f3a/image.png" alt=""></p>
<h4 id="양방향-매핑-시-가장-많이-하는-실수">양방향 매핑 시 가장 많이 하는 실수</h4>
<p>연관관계의 주인에 값을 입력하지 않음</p>
<p><strong>양방향 연관관계 주의 - 실습</strong></p>
<ul>
<li>순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자 </li>
<li>연관관계 편의 메소드를 생성하자 </li>
<li>양방향 매핑시에 무한 루프를 조심하자 </li>
<li>예: toString(), lombok, JSON 생성 라이브러리</li>
</ul>
<p>참고 : 컨트롤러에는 엔티티를 절대 반환하지 말자.
-&gt; 엔티티 변경 시 api가 변경되어 문제가 있다.</p>
<h3 id="양방향-매핑-정리">양방향 매핑 정리</h3>
<ul>
<li>단방향 매핑만으로도 이미 연관관게 매핑은 완료</li>
<li>양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐</li>
<li>JPQL에서 역방향으로 탐색할 일이 많음</li>
<li>단방향 매핑을 잘 하고 양방향은 필요할 때 추가해도 됨 (테이블에 영향을 주지 않음)</li>
</ul>
<h3 id="연관관계의-주인을-정하는-기준">연관관계의 주인을 정하는 기준</h3>
<ul>
<li>비즈니스 로직을 기준으로 주인을 정하지 말자</li>
<li>외래 키의 위치를 기준으로 정하면 헷갈리지 않고, 구현하는 데도 좋다.</li>
</ul>
<h1 id="다양한-연관관계-매핑">다양한 연관관계 매핑</h1>
<p><strong>연관관계 매핑시 고려사항 3가지</strong></p>
<ul>
<li>다중성</li>
<li>단방향, 양방향</li>
<li>연관관계 주인</li>
</ul>
<p><strong>단방향, 양방향</strong></p>
<ul>
<li>테이블<ul>
<li>외래 키 하나로 양쪽 조인 가능</li>
<li>사실 방향이라는 개념이 없음</li>
</ul>
</li>
<li>객체<ul>
<li>참조형 필드가 있는 쪽으로만 참조 가능</li>
<li>한쪽만 참조하면 단방향</li>
<li>양쪽이 서로 참조하면 양방향</li>
</ul>
</li>
</ul>
<p><strong>연관관계의 주인</strong></p>
<ul>
<li>테이블은 외래 키 하나로 두 테이블이 연관관계를 맺음</li>
<li>객체 양방향 관계는 A-&gt;B, B-&gt;A처럼 참조가 2군데</li>
<li>객체 양방향 관계는 참조가 2군데 있음. 둘 중 테이블의 외래키를 관리할 곳을 지정해야함</li>
<li>연관관계의 주인 : 외래키를 관리하는 참조</li>
<li>주인의 반대편 : 외래 키에 영향을 주지 않음, 단순 조회만 가능</li>
</ul>
<h2 id="다대일-n1">다대일 N:1</h2>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/3dbe8095-5ecf-4627-a422-e4a63ebb4942/image.png" alt=""></p>
<ul>
<li>가장 많이 사용하는 연관관계</li>
<li>다대일의 반대는 일대다</li>
</ul>
<p>반대쪽에서 일대다로 추가한다고 해도 테이블에 영향을 주지 않는다. 읽기만 하기 때문 !</p>
<p>정리</p>
<ul>
<li>외래키가 있는 쪽이 연관관계의 주인</li>
<li>양쪽을 서로 참조하도록 개발</li>
</ul>
<h2 id="일대다-1n">일대다 1:N</h2>
<p>여기서는 1이 연관관계의 주인
이러한 모델은 사용을 권장하지는 않는다.
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/c3802cb9-908c-4150-a6c9-2003c17d01ee/image.png" alt=""></p>
<p>정리</p>
<ul>
<li>일대다 단방향은 일대다(1:N)에서 일(1)이 연관관계의 주인</li>
<li>테이블 일대다 관계는 항상 다(N)쪽에 외래키가 있음</li>
<li>객체와 테이블의 차이 때문에 반대편 테이블의 외래키를 관리하는 특이한 구조</li>
<li><code>@JoinColumn</code>을 꼭 사용해야 함. 그렇지 않으면 조인 테이블 방식을 사용함 (중간에 테이블을 하나 추가함)</li>
<li>일대다 단방향 매핑의 단점<ul>
<li>엔티티가 관리하는 외래키가 다른 테이블에 있음</li>
<li>연관관계 관리를 위해 추가로 UPDATE SQL 실행</li>
</ul>
</li>
<li>일대다 단방향 매핑보다는 다대일 양방향 매핑을 사용하자</li>
</ul>
<h2 id="일대일-11">일대일 1:1</h2>
<ul>
<li>일대일 관계는 그 반대도 일대일</li>
<li>주 테이블이나 대상 테이블 중에 외래키 선택 가능<ul>
<li>주 테이블에 외래키</li>
<li>대상 테이블에 외래키</li>
</ul>
</li>
<li>외래키에 데이터베이스 유니크 제약조건 추가
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/2a308c42-1df5-4d15-a26d-75178d983a19/image.png" alt=""></li>
<li>다대일 양방향 매핑과 비슷하다.</li>
</ul>
<h3 id="일대일--주-테이블에-외래키-양방향-정리">일대일 : 주 테이블에 외래키 양방향 정리</h3>
<ul>
<li>다대일 양방향 매핑처럼 외래키가 있는 곳이 연관관계의 주인</li>
<li>반대편은 mappedBy 적용</li>
</ul>
<h3 id="일대일--대상-테이블에-외래키-단방향-정리">일대일 : 대상 테이블에 외래키 단방향 정리</h3>
<ul>
<li>단방향 관계는 JPA 지원 X</li>
<li>양방향 관계는 지원</li>
</ul>
<h3 id="일대일--대상-테이블에-외래키-양방향">일대일 : 대상 테이블에 외래키 양방향</h3>
<p>사실 일대일 주 테이블에 외래키 양방향과 매핑 방법은 같음</p>
<h3 id="정리">정리</h3>
<ul>
<li><strong>주 테이블에 외래키</strong><ul>
<li>주 객체가 대상 객체의 참조를 가지는 것처럼 주 테이블에 외래키를 두고 대상 테이블을 찾음</li>
<li>객체지향 개발자 선호</li>
<li>JPA 매핑 편리</li>
<li>장점 : 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인 가능</li>
<li>단점 : 값이 없으면 외래키에 null gjdyd</li>
</ul>
</li>
<li><strong>대상 테이블에 외래키</strong><ul>
<li>대상 테이블에 외래키가 존재</li>
<li>전통적인 데이터베이스 개발자 선호</li>
<li>장점 : 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조 유지</li>
<li>단점 : 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시 로딩됨</li>
</ul>
</li>
</ul>
<h2 id="다대다-nm">다대다 N:M</h2>
<p>실무에서는 잘 쓰지 않는다.</p>
<ul>
<li>RDB는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없음</li>
<li>연결 테이블을 추가해서 일대다, 다대일 관계로 풀어내야함</li>
<li><code>@ManyToMany</code> 사용</li>
<li><code>@JoinTable</code>로 연결 테이블 지정</li>
</ul>
<p>한계</p>
<ul>
<li>다대다 매핑 : 단방향 양방향 가능</li>
<li>편리해 보이지만 실무에서 사용 X</li>
<li>연결 테이블이 단순히 연결만 하고 끝나지 않음</li>
<li>주문시간, 수량 같은 데이터가 들어올 수 있음</li>
</ul>
<h3 id="한계-극복">한계 극복</h3>
<ul>
<li>연결 테이블용 엔티티 추가 (연결 테이블을 엔티티로 승격)</li>
<li><code>@ManyToMany</code> -&gt; <code>@OneToMany</code>, <code>@ManyToOne</code>
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/65c07f87-df90-4639-b960-445cee459c3f/image.png" alt=""></li>
</ul>
<h1 id="고급-매핑">고급 매핑</h1>
<h2 id="상속관계-매핑">상속관계 매핑</h2>
<ul>
<li><p>관계형 데이터베이스는 상속 관계 X</p>
</li>
<li><p>슈퍼타입 서브타입 관계라는 모델링 기법이 객체 상속과 유사</p>
</li>
<li><p>상속관계 매핑 : 객체의 상속과 구조와 DB의 슈퍼타입 서브타입 관계를 매핑
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/b7b236a8-9c5a-4fb3-8c19-688f29d2db76/image.png" alt=""></p>
</li>
<li><p>슈퍼타입 서브타입 논리 모델을 실제 물리 모델로 구현하는 방법</p>
<ul>
<li>각각 테이블로 변환 -&gt; 조인 전략
  <img src="https://velog.velcdn.com/images/hyeok_b2ng/post/10dece4d-e30e-4378-8be4-d0c419b5b036/image.png" alt=""></li>
<li>통합 테이블로 변환 -&gt; 단일 테이블 전략
  <img src="https://velog.velcdn.com/images/hyeok_b2ng/post/499f6a2e-633f-4431-a29b-f195ca209bcb/image.png" alt=""></li>
<li>서브타입 테이블로 변환 -&gt; 구현 클래스마다 테이블 전략
  <img src="https://velog.velcdn.com/images/hyeok_b2ng/post/602ef0ea-eea7-4f94-a327-7f3cf0244f38/image.png" alt=""></li>
</ul>
</li>
</ul>
<p>주요 어노테이션</p>
<ul>
<li><code>@Inheritance(strategy = InheritanceType.XXX)</code><ul>
<li>JOINED : 조인 전략</li>
<li>SINGLE_TABLE : 단일 테이블 전략</li>
<li>TABLE_PER_CLASS : 구현 클래스마다 테이블 전략</li>
</ul>
</li>
<li><code>DiscriminatorColumn(name=&quot;DTYPE&quot;)</code></li>
<li><code>@DiscriminatorValue(&quot;XXX&quot;)</code></li>
</ul>
<h3 id="조인전략">조인전략</h3>
<ul>
<li>장점<ul>
<li>테이블 정규화</li>
<li>외래 키 참조 무결성 제약조건 활용가능</li>
<li>저장공간 효율화</li>
</ul>
</li>
<li>단점<ul>
<li>조회시 조인을 많이 사용, 성능 저하</li>
<li>조회 쿼리가 복잡함</li>
<li>데이터 저장시 INSERT SQL 2번 호출<h3 id="단일-테이블-전략">단일 테이블 전략</h3>
</li>
</ul>
</li>
<li>장점<ul>
<li>조인이 필요 없으므로 일반적으로 조회 성능이 빠름</li>
<li>조회 쿼리가 단순함</li>
</ul>
</li>
<li>단점<ul>
<li>자식 엔티티가 매핑한 컬럼은 모두 null 허용</li>
<li>단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다. 상황에 따라서 조회 성능이 오히려 느려질 수 있다.<h3 id="구현-클래스마다-테이블-전략">구현 클래스마다 테이블 전략</h3>
이 전략은 데이터베이스 설계자와 ORM 전문가 둘 다 추천 X</li>
</ul>
</li>
<li>장점<ul>
<li>서브 타입을 명확하게 구분해서 처리할 때 효과적</li>
<li>not null 제약조건 사용 가능</li>
</ul>
</li>
<li>단점<ul>
<li>여러 자식 테이블을 함께 조회할 때 성능이 느림(UNION SQL 필요)</li>
<li>자식 테이블을 통합해서 쿼리하기 어려움</li>
</ul>
</li>
</ul>
<h2 id="mappedsuperclass"><code>@MappedSuperclass</code></h2>
<p>공통 매핑 정보가 필요할 때 사용 (예시 id, name)
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/751afe73-67e8-4b65-b09c-0ddee29d0570/image.png" alt=""></p>
<ul>
<li>상속관계 매핑X</li>
<li>엔티티X, 테이블과 매핑X</li>
<li>부모 클래스를 상속 받는 자식 클래스에 매핑 정보만 제공</li>
<li>조회, 검색 불가</li>
<li>직접 생성해서 사용할 일이 없으므로 추상 클래스 권장</li>
<li>테이블과 관계 없고, 단순히 엔티티가 공통으로 사용하는 매핑 정보를 모으는 역할</li>
<li>주로 등록일, 수정일, 등록자, 수정자 같은 전체 엔티티에서 공통으로 적용하는 정보를 모을 때 사용</li>
<li>참고 : <code>@Entity</code> 클래스는 엔티티나 <code>@MappedSuperclass</code>로 지정한 클래스만 상속 가능</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA 기본편 - 엔티티 매핑]]></title>
            <link>https://velog.io/@hyeok_b2ng/JPA-%EA%B8%B0%EB%B3%B8%ED%8E%B8-%EC%97%94%ED%8B%B0%ED%8B%B0-%EB%A7%A4%ED%95%91</link>
            <guid>https://velog.io/@hyeok_b2ng/JPA-%EA%B8%B0%EB%B3%B8%ED%8E%B8-%EC%97%94%ED%8B%B0%ED%8B%B0-%EB%A7%A4%ED%95%91</guid>
            <pubDate>Tue, 16 Jul 2024 14:56:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 게시글의 출처는 인프런 김영한님의 강의입니다.
강의명은 자바 ORM 표준 JPA 프로그래밍 - 기본편 입니다.
출처 : <a href="https://www.inflearn.com/course/ORM-JPA-Basic">https://www.inflearn.com/course/ORM-JPA-Basic</a></p>
</blockquote>
<h2 id="객체와-테이블-매핑">객체와 테이블 매핑</h2>
<h3 id="엔티티-매핑-소개">엔티티 매핑 소개</h3>
<ul>
<li>객체와 테이블 매핑 : <code>@Entity</code>, <code>@Table</code></li>
<li>필드와 컬럼 매핑 : <code>@Column</code></li>
<li>기본 키 매핑 : <code>@Id</code></li>
<li>연관관계 매핑 : <code>@ManyToOne</code>, <code>@JoinColumn</code><h3 id="객체와-테이블-매핑-1">객체와 테이블 매핑</h3>
<code>@Entity</code></li>
<li>JPA가 관리, 엔티티라 한다.</li>
<li>JPA를 사용해서 테이블과 매핑할 클래스는 <code>@Entity</code> 필수</li>
<li>주의<ul>
<li>기본 생성자 필수</li>
<li>final 클래스, enum, interface, inner 클래스 사용 X</li>
<li>저장할 필드에 final 사용 X</li>
</ul>
</li>
</ul>
<p>@Entity 속성 정리
<code>name</code></p>
<ul>
<li>JPA에서 사용할 엔티티 이름을 지정한다.</li>
<li>기본값: 클래스 이름을 그대로 사용(예: Member) </li>
<li>같은 클래스 이름이 없으면 가급적 기본값을 사용한다.</li>
</ul>
<p><code>@Table</code>
엔티티와 매핑할 테이블 지정
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/cc3b8c2e-b89d-42e2-a57b-1f702bdbb028/image.png" alt=""></p>
<h2 id="데이터베이스-스키마-자동-생성">데이터베이스 스키마 자동 생성</h2>
<ul>
<li>DDL을 애플리케이션 실행 시점에 자동 생성</li>
<li>테이블 중심 -&gt; 객체 중심</li>
<li>데이터베이스 방언을 활용해서 데이터베이스에 맞는 적잘한 DDL 생성</li>
<li>이렇게 생성된 DDL은 <strong>개발 장비</strong>에서만 사용</li>
<li>생성된 DDL은 운영서버에서는 사용하지 않거나, 적절히 다듬은 후 사용
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/a3acdee9-b6c6-41a0-b38f-01defacb5bd6/image.png" alt=""></li>
</ul>
<h3 id="주의">주의</h3>
<ul>
<li><strong>운영 장비에는 절대로 사용하면 안된다.</strong></li>
<li>개발 초기 단계는 create 또는 update</li>
<li>테스트 서버는 update 또는 validate</li>
<li>스테이징과 운영 서버는 validate 또는 none</li>
</ul>
<h3 id="ddl-생성-기능">DDL 생성 기능</h3>
<ul>
<li>제약조건 추가 : 회원 이름은 필수, 10자 초과 X<ul>
<li><code>@Column(nullable = false, length = 10)</code></li>
</ul>
</li>
<li>유니크 제약 조건 추가</li>
<li>DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고 JPA 실행 로직에는 영향을 주지 않는다.</li>
</ul>
<h2 id="필드와-컬럼-매핑">필드와 컬럼 매핑</h2>
<pre><code class="language-java">package hellojpa;
import javax.persistence.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
@Entity
public class Member {
@Id
private Long id;
@Column(name = &quot;name&quot;)
private String username;
private Integer age;
@Enumerated(EnumType.STRING)
private RoleType roleType;
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
@Lob
private String description;
}</code></pre>
<h3 id="매핑-어노테이션-정리">매핑 어노테이션 정리</h3>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/293760ce-480a-4dcd-a23c-da184f320786/image.png" alt=""></p>
<h3 id="column"><code>@Column</code></h3>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/befe0858-cf54-4207-ae48-b147318bd562/image.png" alt=""></p>
<h3 id="enumerated"><code>@Enumerated</code></h3>
<ul>
<li>Java enum타입을 매핑할 때 사용</li>
</ul>
<p>Enum 타입을 매핑할 때 주의사항
: ORDINAL 사용 X
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/640409dc-94bb-4539-acd2-0004c22efafd/image.png" alt=""></p>
<h3 id="temporal"><code>@Temporal</code></h3>
<p>날짜 타입 (java.util.Date, java.util.Calendar)을 매핑할 때 사용
참고 : LocalDate, LocalDateTime을 사용할 때는 생략 가능
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/35f462f6-4c88-4b69-a8cc-b22be2614faa/image.png" alt=""></p>
<h3 id="lob"><code>@Lob</code></h3>
<p>데이터베이스 BLOB, CLOB과 매핑</p>
<ul>
<li><code>@Lob</code>에는 지정할 수 있는 속성이 없다.</li>
<li>매핑하는 필드 타입이 문자면 CLOB 매핑, 나머지는 BLOB 매핑<ul>
<li>CLOB : String, char[], java.sql.CLOB</li>
<li>BLOB : byte[], java.sql.BLOB<h3 id="transient"><code>@Transient</code></h3>
</li>
</ul>
</li>
<li>필드 매핑 X</li>
<li>데이터베이스에 저장X, 조회X</li>
<li>주로 메모리상에서만 임시로 어떤 값을 보관하고 싶을 때 사용</li>
</ul>
<h2 id="기본-키-매핑">기본 키 매핑</h2>
<h3 id="기본-키-매핑-어노테이션">기본 키 매핑 어노테이션</h3>
<ul>
<li><code>@Id</code></li>
<li><code>@GeneratedValue</code><pre><code class="language-java">@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;</code></pre>
</li>
<li>직접 할당 : @Id</li>
<li>자동 생성 (@GeneratedValue)<ul>
<li>IDENTITY: 데이터베이스에 위임, MYSQL</li>
<li>SEQUENCE: 데이터베이스 시퀀스 오브젝트 사용, ORACLE
@SequenceGenerator 필요</li>
<li>TABLE: 키 생성용 테이블 사용, 모든 DB에서 사용
@TableGenerator 필요</li>
<li>AUTO: 방언에 따라 자동 지정, 기본값<h3 id="identity-전략---특징">IDENTITY 전략 - 특징</h3>
</li>
</ul>
</li>
<li>기본 키 생성을 데이터베이스에 위임</li>
<li>주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용 (예: MySQL의 AUTO_ INCREMENT)</li>
<li>JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행</li>
<li>AUTO_ INCREMENT는 데이터베이스에 INSERT SQL을 실행
한 이후에 ID 값을 알 수 있음</li>
<li>IDENTITY 전략은 em.persist() 시점에 즉시 INSERT SQL 실행 하고 DB에서 식별자를 조회<h3 id="sequence-전략---특징">SEQUENCE 전략 - 특징</h3>
</li>
<li>데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트(예: 오라클 시퀀스)</li>
<li>오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/79ec6678-95fc-4e36-aa86-0d0c1eb86a84/image.png" alt=""></li>
</ul>
<h3 id="table-전략">TABLE 전략</h3>
<ul>
<li>키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략</li>
<li>장점 : 모든 데이터베이스에 적용가능</li>
<li>단점 : 성능
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/d9752bb2-0fa7-49d5-9685-cca6fcd7fe0a/image.png" alt=""></li>
</ul>
<h3 id="권장하는-식별자-전략">권장하는 식별자 전략</h3>
<ul>
<li>기본키 제약 조건 : null 아님, 유일, 변하면 안된다.</li>
<li>미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. 대리키(대체키)를 사용하자.</li>
<li>예를 들어 주민등록번호도 기본 키로 적절하지 않다.</li>
<li>권장 : Long형 + 대체키 + 키 생성전략 사용</li>
</ul>
<p>참고
IDENTITY 전략을 사용할 경우 em.persist를 실행할 시 insert가 실행된다. 앞강의에서의 영속성 컨텍스트와 다르다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA 기본편 - 영속성 관리 (내부 동작 방식)]]></title>
            <link>https://velog.io/@hyeok_b2ng/JPA-%EC%98%81%EC%86%8D%EC%84%B1-%EA%B4%80%EB%A6%AC-%EB%82%B4%EB%B6%80-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D</link>
            <guid>https://velog.io/@hyeok_b2ng/JPA-%EC%98%81%EC%86%8D%EC%84%B1-%EA%B4%80%EB%A6%AC-%EB%82%B4%EB%B6%80-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D</guid>
            <pubDate>Tue, 16 Jul 2024 13:14:43 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 게시글의 출처는 인프런 김영한님의 강의입니다.
강의명은 자바 ORM 표준 JPA 프로그래밍 - 기본편 입니다.
출처 : <a href="https://www.inflearn.com/course/ORM-JPA-Basic">https://www.inflearn.com/course/ORM-JPA-Basic</a></p>
</blockquote>
<h2 id="영속성-컨텍스트-1">영속성 컨텍스트 1</h2>
<h3 id="jpa에서-가장-중요한-2가지">JPA에서 가장 중요한 2가지</h3>
<ul>
<li>객체와 관계형 데이터베이스 매핑하기 (Object Relational Mapping)</li>
<li><strong>영속성 컨텍스트</strong></li>
</ul>
<h3 id="엔티티-매니저-팩토리와-엔티티-매니저">엔티티 매니저 팩토리와 엔티티 매니저</h3>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/8d4f591c-49dc-4a3f-95bb-cb26fde5e5f4/image.png" alt=""></p>
<h3 id="영속성-컨텍스트">영속성 컨텍스트</h3>
<ul>
<li>JPA를 이해하는데 가장 중요한 용어</li>
<li>&quot;엔티티를 영구 저장하는 환경&quot;</li>
<li><code>EntityManager.persist(entity);</code></li>
</ul>
<h3 id="엔티티의-생명주기">엔티티의 생명주기</h3>
<ul>
<li>비영속 (new/transient) : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태</li>
<li>영속 (managed) : 영속성 컨텍스트에 관리되는 상태</li>
<li>준영속 (detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태</li>
<li>삭제 (removed) : 삭제된 상태
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/5f7bf834-8592-485c-914d-13d444085d11/image.png" alt=""></li>
</ul>
<h3 id="비영속">비영속</h3>
<pre><code class="language-java">Member member = new Member();
member.setId(&quot;member1&quot;);
member.setUsername(&quot;회원1&quot;);</code></pre>
<p>생성만 한 상태</p>
<h3 id="영속">영속</h3>
<pre><code class="language-java">// 객체를 생성한 상태 (비영속)
Member member = new Member();
member.setId(&quot;member1&quot;);
member.setUsername(&quot;회원1&quot;);

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

// 객체를 저장한 상태 (영속)
em.persist(member);</code></pre>
<h3 id="준영속-삭제">준영속, 삭제</h3>
<pre><code class="language-java">// 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);

// 객체를 삭제한 상태 (삭제)
em.remove(member);</code></pre>
<h3 id="영속성-컨텍스트의-이점">영속성 컨텍스트의 이점</h3>
<ul>
<li>1차 캐시</li>
<li>동일성(identity) 보장</li>
<li>트랜잭션을 지원하는 쓰기 지연</li>
<li>변경 감지</li>
<li>지연 로딩</li>
</ul>
<h2 id="영속성-컨텍스트-2">영속성 컨텍스트 2</h2>
<h3 id="엔티티-조회-1차-캐시">엔티티 조회, 1차 캐시</h3>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/6e3adc31-3b4b-4ba6-bfed-47e968f08b83/image.png" alt=""></p>
<p><strong>1차캐시에서 조회</strong></p>
<pre><code class="language-java">Member member = new Member();
member.setId(&quot;member1&quot;);
member.setUsername(&quot;회원1&quot;);

// 1차 캐시에 저장
em.persist(member);

// 1차 캐시에서 조회
Member findMember = em.find(Member.class, &quot;member1&quot;);</code></pre>
<p><strong>데이터베이스에서 조회</strong>
<code>Member findMember2 = em.find(Member.class, &quot;member2&quot;);</code>
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/2415fb53-af98-4b4e-a2b1-96b051370d75/image.png" alt=""></p>
<p>=&gt; 데이터 베이스 한 트랜잭션안에서만 동작하므로 큰 성능의 이점은 없다.
but, 비즈니스가 정말 복잡하다면 이득이 있다.</p>
<h3 id="영속-엔티티의-동일성-보장">영속 엔티티의 동일성 보장</h3>
<p>1차 캐시로 반복 가능한 읽기등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공</p>
<h3 id="엔티티-등록-트랜잭션을-지원하는-쓰기-지원">엔티티 등록, 트랜잭션을 지원하는 쓰기 지원</h3>
<pre><code class="language-java">EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin();  // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); </code></pre>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/a95b3e36-5173-4256-9e38-cb2b3ec299ae/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/e97313e5-e9f0-4bdb-9572-95ccb24c9d08/image.png" alt=""></p>
<p>이렇듯 DB에 추후에 저장한다.</p>
<h3 id="엔티티-수정-변경감지">엔티티 수정, 변경감지</h3>
<pre><code class="language-java">EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();  // [트랜잭션] 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, &quot;memberA&quot;);
// 영속 엔티티 데이터 수정
memberA.setUsername(&quot;hi&quot;);
memberA.setAge(10);
//em.update(member) 이런 코드가 있어야 하지 않을까?
transaction.commit();</code></pre>
<h3 id="엔티티-삭제">엔티티 삭제</h3>
<pre><code class="language-java">// 삭제 대상 엔티티 조회
Member memberA = em.find(Member.class, &quot;memberA&quot;);

em.remove(memberA);    // 엔티티 삭제</code></pre>
<h2 id="플러시">플러시</h2>
<p>영속성 컨텍스트의 변경내용을 데이터베이스에 반영</p>
<h3 id="플러시-발생">플러시 발생</h3>
<ul>
<li>변경 감지</li>
<li>수정된 엔티티 쓰기 지연 SQL 저장소에 등록</li>
<li>쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송 (등록, 수정, 삭제 쿼리)</li>
</ul>
<h3 id="영속성-컨텍스트를-플러시하는-방법">영속성 컨텍스트를 플러시하는 방법</h3>
<ul>
<li>em.flush() : 직접 호출</li>
<li>트랜잭션 커밋 : 플러시 자동 호출</li>
<li>JPQL 쿼리 실행 : 플러시 자동 호출</li>
</ul>
<p>플러시를 하면 1차캐시가 다 지워지는가?
-&gt; No, 그대로 다 유지가 됨.</p>
<h3 id="jpql-쿼리-실행-시-플러시가-자동으로-호출되는-이유">JPQL 쿼리 실행 시 플러시가 자동으로 호출되는 이유</h3>
<pre><code class="language-java">em.persist(memberA);
em.persist(memberB);
em.persist(memberC);

//중간에 JPQL 실행
query = em.createQuery(&quot;select m from Member m&quot;, Member.class);
List&lt;Member&gt; members= query.getResultList();</code></pre>
<h3 id="플러시-모드-옵션">플러시 모드 옵션</h3>
<p><code>em.setFlushMode(FlushModeType.COMMIT);</code></p>
<ul>
<li>FlushModeType.AUTO : 커밋이나 쿼리를 실행할 때 플러시 (기본값) </li>
<li>FlushModeType.COMMIT : 커밋할 때만 플러시</li>
</ul>
<p>-&gt; 잘 사용을 하지않음</p>
<h3 id="정리">정리</h3>
<p>플러시 ?</p>
<ul>
<li>영속성 컨텍스트를 비우지 않음</li>
<li>영속성 컨텍스트의 변경내용을 데이터베이스에 동기화</li>
<li>트랜잭션이라는 작업 단위가 중요 -&gt; 커밋 직전에만 동기화하면 됨</li>
</ul>
<h2 id="준영속-상태">준영속 상태</h2>
<ul>
<li>영속 -&gt; 준영속</li>
<li>영속 상태의 엔티티가 영속성 컨텍스트에서 분리 (detached)</li>
<li>영속성 컨텍스트가 제공하는 기능을 사용 못함</li>
</ul>
<h3 id="준영속-상태로-만드는-방법">준영속 상태로 만드는 방법</h3>
<ul>
<li><code>em.detach(entity)</code> : 특정 엔티티만 준영속 상태로 전환</li>
<li><code>em.clear()</code> : 영속성 컨텍스트를 완전히 초기화</li>
<li><code>em.close()</code> : 영속성 컨텍스트를 종료</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA 기본편 - JPA 소개]]></title>
            <link>https://velog.io/@hyeok_b2ng/%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-JPA-%EC%86%8C%EA%B0%9C</link>
            <guid>https://velog.io/@hyeok_b2ng/%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-JPA-%EC%86%8C%EA%B0%9C</guid>
            <pubDate>Sun, 14 Jul 2024 13:36:19 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 게시글의 출처는 인프런 김영한님의 강의입니다.
강의명은 자바 ORM 표준 JPA 프로그래밍 - 기본편 입니다.
출처 : <a href="https://www.inflearn.com/course/ORM-JPA-Basic">https://www.inflearn.com/course/ORM-JPA-Basic</a></p>
</blockquote>
<h2 id="sql-중심적인-개발의-문제점">SQL 중심적인 개발의 문제점</h2>
<p>관계형 DB에 데이터를 저장하기 위해서는 똑같은 SQL문을 계속 작성해야한다.</p>
<ul>
<li>무한 반복</li>
<li>지루한 코드</li>
</ul>
<h3 id="객체와-관계형-데이터베이스의-차이">객체와 관계형 데이터베이스의 차이</h3>
<p><strong>상속</strong>
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/c6be2f12-b7a3-41f7-8030-c09a5e73d718/image.png" alt="">
Album 조회</p>
<ol>
<li>각각의 테이블에 따른 조인 SQL 작성</li>
<li>각각의 객체 생성</li>
<li>상상만 해도 복잡</li>
<li>그래서 DB에 저장할 객체에는 상속 관계를 쓰지 않는다.</li>
</ol>
<p>-&gt; But, Java Collection에 저장하면? list.add() 조회도 list.get() 부모 타입으로 조회 후 다형성 활용도 가능</p>
<p><strong>연관관계</strong></p>
<ul>
<li>객체는 참조를 사용 : member.getTeam()</li>
<li>테이블은 외래 키를 사용 : JOIN ON M.TEAM_ID = T.TEAM_ID
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/0f104e38-0bbf-4474-ab61-c53cc0185f16/image.png" alt=""></li>
</ul>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/526b0bce-dc6e-4e1e-8b25-6adf6300fc3e/image.png" alt="">
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/a6fe9384-7a82-44ad-8e73-edf04aacd727/image.png" alt=""></p>
<p>즉, 객체답게 모델링 할수록 매핑작업만 늘어난다.</p>
<p>따라서 객체를 자바 컬렉션에 저장하듯이 DB에 저장할 순 없을까?
JPA로 해결.</p>
<h2 id="jpa-소개">JPA 소개</h2>
<p><strong>JPA</strong></p>
<ul>
<li>Java Persistence API</li>
<li>자바 진영의 ORM 기술 표준</li>
</ul>
<p><strong>ORM</strong></p>
<ul>
<li>Object-relational mapping (객체 관계 매핑)</li>
<li>객체는 객체대로 설계</li>
<li>관계형 데이터베이스는 관계형 데이터베이스대로 설계</li>
<li>ORM 프레임워크가 중간에서 매핑</li>
<li>대중적인 언어에는 ORM이 존재</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/c2aac8ed-45a5-4f26-a301-42a88316d5d5/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/68087ab0-dbb2-4ef5-93d6-e0a522fde4d8/image.png" alt=""></p>
<p>JPA는 표준 명세</p>
<ul>
<li>JPA는 인터페이스의 모음</li>
<li>JPA 2.1 표준 명세를 구현한 3가지 구현체</li>
<li>하이버네이트, EclipseLink, DataNucleus
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/5ef6f641-64c1-4278-8112-77243994c408/image.png" alt=""></li>
</ul>
<p>JPA를 왜 사용해야 하는가 ?</p>
<ul>
<li>SQL 중심적인 개발에서 객체 중심으로 개발</li>
<li>생산성</li>
<li>유지보수</li>
<li>패러다임의 불일치 해결</li>
<li>성능</li>
<li>데이터 접근 추상화와 벤더 독립성</li>
<li>표준</li>
</ul>
<p>생산성 - JPA와 CRUD</p>
<ul>
<li>저장 : jpa.persist(member)</li>
<li>조회 : Member member = jpa.find(memberId)</li>
<li>수정 : member.setName(&quot;변경할 이름&quot;)</li>
<li>삭제 : jpa.remove(member)</li>
</ul>
<p><strong>유지보수</strong>
기존 : 필드 변경시 모든 SQL 수정</p>
<h3 id="jpa와-패러다임의-불일치-해결">JPA와 패러다임의 불일치 해결</h3>
<ol>
<li>JPA와 상속</li>
<li>JPA와 연관관계</li>
<li>JPA와 객체 그래프 탐색</li>
<li>JPA와 비교하기</li>
</ol>
<p><strong>JPA와 상속</strong>
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/0f344e85-3596-48c5-893f-f7981ff30564/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/baa84b0f-0a6a-40c1-b2b5-4a227b9b0e41/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/e4dac00a-d566-4f88-8e8f-a3578931bf67/image.png" alt=""></p>
<p><strong>JPA와 연관관계</strong>
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/497e9e08-df2a-4908-b744-76b9b959ca0b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/7679fdb6-e78b-47ca-bb7e-6e625824f90b/image.png" alt=""></p>
<p><strong>JPA와 비교하기</strong>
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/e2354704-67d8-43f8-addf-a4eaa7779f34/image.png" alt=""></p>
<h3 id="jpa의-성능-최적화-기능">JPA의 성능 최적화 기능</h3>
<ol>
<li>1차 캐시와 동일성 보장</li>
<li>트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)</li>
<li>지연 로딩(Lazy Loading)</li>
</ol>
<h4 id="1차-캐시와-동일성-보장">1차 캐시와 동일성 보장</h4>
<ol>
<li>같은 트랜잭션 안에서는 같은 엔티티를 반환 - 약간의 조회 성능 향상</li>
<li>DB Isolation Level이 Read Commit이어도 애플리케이션에서 Repeatabel Read 보장
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/a4d48833-fcd2-4a7c-87ff-978f3850aea9/image.png" alt=""></li>
</ol>
<h4 id="트랜잭션을-지원하는-쓰기-지연">트랜잭션을 지원하는 쓰기 지연</h4>
<ol>
<li><p>트랜잭션을 커밋할 때까지 INSERT SQL을 모음</p>
</li>
<li><p>JDBC BATCH SQL 기능을 사용해서 한번에 SQL 전송
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/f5c72a6a-78e8-4543-9c74-8bc75c4c446b/image.png" alt=""></p>
</li>
<li><p>UPDATE, DELETE로 인한 로우락 시간 최소화</p>
</li>
<li><p>트랜잭션 커밋 시 UPDATE, DELETE SQL 실행하고, 바로 커밋
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/4a9fa76b-dd29-4fcd-b9d8-accf94939026/image.png" alt=""></p>
</li>
</ol>
<h4 id="지연-로딩">지연 로딩</h4>
<ul>
<li>지연로딩 : 객체가 실제 사용될 때 로딩</li>
<li>즉시로딩 : JOIN SQL로 한번에 연관된 객체까지 미리 조회
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/03d59fe3-1ae6-49f0-9cd3-1b277b68f943/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Agile 개발방법론]]></title>
            <link>https://velog.io/@hyeok_b2ng/Agile-%EA%B0%9C%EB%B0%9C%EB%B0%A9%EB%B2%95%EB%A1%A0</link>
            <guid>https://velog.io/@hyeok_b2ng/Agile-%EA%B0%9C%EB%B0%9C%EB%B0%A9%EB%B2%95%EB%A1%A0</guid>
            <pubDate>Thu, 11 Jul 2024 00:28:25 GMT</pubDate>
            <description><![CDATA[<h2 id="agile의-정의">Agile의 정의</h2>
<ul>
<li>사전적 의미 &quot;신속한&quot;, &quot;날렵한&quot;, &quot;기민한&quot;이라는 뜻을 가짐</li>
<li>SW방법론의 내포적 의미, 짧은 주기의 개발 단위를 반복하여 프로젝트를 완성해 가는 것 !</li>
<li>어떻게 반복하라는 것인가? =&gt; 스프린트 단위로 디자인(기획) -&gt; 개발 -&gt; 테스트 반복<h3 id="agile-방법론의-특징">Agile 방법론의 특징</h3>
</li>
<li>고객과 개발자의 지속적인 소통으로 변화하는 요구사항을 신속하게 반영 가능</li>
<li>개인 보다는 팀의 목적을 우선시 하며, 고객의 의견을 가장 높은 가치로 둔다.</li>
<li>팀원들의 주기적인 회의 및 제품 시현을 통해 잠재적인 버그 수정과 미흡한 기능 추가 기능</li>
<li>프로젝트를 진행하면서 고객으로부터 즉각적인 피드백을 통한 수정, 보완 가능</li>
<li>팀원들의 주도적이며 자발적인 개발 문화로 프로그램 전체의 품질 향상에 기여 가능<h2 id="애자일-개발의-등장-배경">애자일 개발의 등장 배경</h2>
<h3 id="폭포수-개발론">폭포수 개발론</h3>
가장 오래된 SW 개발론</li>
<li>각 작업이 마치 폭포수가 내려가듯이 작업 절차가 진행됨</li>
<li>한 단계가 마무리 되기 전 다음 단계로 진행이 되지 못함</li>
<li>속도가 느리고 유연하지 못하다는 단점을 지님</li>
<li>요구사항이 바뀌거나 수정하려면 다시 맨 처음부터 수정해야하는 불편함이 존재.</li>
</ul>
<p>=&gt; 따라서 Agile이 등장하게 되었다.</p>
<h3 id="폭포수-개발론-vs-agile-방법론">폭포수 개발론 vs. Agile 방법론</h3>
<table>
<thead>
<tr>
<th align="center"></th>
<th align="left">워터폴 방법론</th>
<th align="left">애자일 방법론</th>
</tr>
</thead>
<tbody><tr>
<td align="center">요구사항</td>
<td align="left">초기 단계에서 명확하게 파악</td>
<td align="left">지속적으로 요구사항을 반영</td>
</tr>
<tr>
<td align="center">특징</td>
<td align="left">요구사항 분석 및 문서화에 시간이 오래 소요될 수 있음, 고객의 요구사항이나 피드백에 대한 대응이 느림</td>
<td align="left">문제 해결을 위해 팀원들 간 소통과 협업이 중요</td>
</tr>
<tr>
<td align="center">적합한 경우</td>
<td align="left">요구 사항이 매우 디테일하고 명확한 프로젝트, 변화가 적고 유동성이 낮은 프로젝트</td>
<td align="left">사용자의 피드백을 수집하고, 빠르게 개선이 필요한 경우, 요구사항이 변경되거나 문제 발생이 잦은 프로젝트</td>
</tr>
</tbody></table>
<h2 id="agile의-장단점">Agile의 장,단점</h2>
<h3 id="agile의-장점">Agile의 장점</h3>
<ul>
<li>프로젝트의 계획에 걸리는 시간을 최소화 가능</li>
<li>반복적인 테스트로 잠정적인 버그를 쉽고 빠르게 개선 가능</li>
<li>계획 변경이나 기능 추가에 유연함</li>
<li>고객의 요구사항에 대한 즉각적인 피드백에 유연하며 프로토타입 모델을 빠르게 출시 가능</li>
<li>빠듯한 출시 일정에 비교적 빠르게 제품 출시 가능<h3 id="agile의-단점">Agile의 단점</h3>
</li>
<li>확정되지 않은 계획 및 잦은 요구 사항 변경으로 인한 반복적인 유지보수 작업이 많음</li>
<li>고객의 요구사항 및 계획이 크게 변경 될 경우 모델 자체가 무너질 수 있음</li>
<li>팀 중심의 개발으로 인한 공통 작업의 리소스 투입이 많음 (회의, 로그 등)</li>
<li>반복적인 업무로 개발 속도는 빠를 수 있으나 번아웃 현상이 쉽게 올 수 있음</li>
<li>확정되지 않은 계획으로 인해 개발 진행에 대한 정확한 이해 부족 발생 가능<h2 id="agile-프레임워크">Agile 프레임워크</h2>
<h3 id="최소-기능-제품-minimum-viable-product">최소 기능 제품 (Minimum Viable Product)</h3>
</li>
<li><em>고객이 원하는 제품의 최소한의 기능을 정의*</em></li>
<li>최소 비용으로 빠르게 만드는 핵심기능만을 담은 제품</li>
<li>수익성 판단을 위한 시간과 비용 절감측면에서 매우 효율적</li>
<li>사업 리스크 최소화 - 실패하더라도 괜찮아 !<h3 id="스프린트-sprint">스프린트 (Sprint)</h3>
</li>
<li><em>애자일에서의 스프린트 정의*</em></li>
<li>팀이 일정량의 작업을 완료하기 위해 정해진 짧은 기간</li>
<li>기업에서 보통 2주 정도의 기간으로 시작</li>
<li>스프린트 계획 회의 - 스프린트 기간동안 해야할 일들을 정리하고 달성하기 위한 방법 회의<h3 id="스크럼-scrum">스크럼 (Scrum)</h3>
</li>
<li><em>정해진 스프린트 내에 실제 행해져야 하는 개발 업무*</em></li>
<li>제품 백로그 작성 : 고객 및 프로젝트 이해관계자들의 의견을 취합하고 업무의 우선순위를 매기는 작업</li>
<li>스프린트 백로그 작성 : 스프린트 내에 각 팀원들이 해야 할 업무의 리스트를 만드는 작업</li>
<li>데일리 스크럼 미팅 : 만들어진 리스트를 바탕으로 매일 진행한 업무를 보고하고 공유하는 작업</li>
<li>개발 및 테스트 : 팀원들이 실제 맡은 영역을 개발, 테스트 하는 작업</li>
<li>스프린트 리뷰 및 회고 : 스프린트 내에 만들어진 결과물을 통해 장,단점을 분석하고 더 나은 방향으로 개선하는 작업<h3 id="테스트-주도-개발-test-driven-development">테스트 주도 개발 (Test Driven Development)</h3>
</li>
<li><em>모든 개발이 아닌 테스트 주도적 개발*</em></li>
<li>모든 것을 만들고 예측하기에는 시간이 부족</li>
<li>실제 필요한 기능만 만들고 그 기능이 제대로 동작하는 지 코드 기반으로 테스트</li>
<li>잘 동작하는 것이 확인되면 코드 리팩토링 및 모듈화</li>
</ul>
<p>테스트 주도적 개발에 도움이 되는 툴</p>
<ul>
<li>생성형 AI를 이용한 특정 기능의 코드를 빠르게 구현한 후 모듈화</li>
<li>Copliot등의 코드 자동생성Tool을 이용하여 기능 구현 코드를 쉽게 학<h3 id="데브옵스-dev--ops">데브옵스 (Dev + Ops)</h3>
</li>
<li><em>개발과 운영을 동시에 하자*</em></li>
<li>개발 + 운영의 합성어</li>
<li>새로운 소프트웨어 기능 개선, 버그 수정 시 바로 배포함으로서 빠른 피드백 가능</li>
<li>지속적 통합 및 연속 배포(CI/CD) 구축<h2 id="정리-및-요약">정리 및 요약</h2>
</li>
<li>애자일 방법론은 스프린트 단위로 디자인 -&gt; 개발 -&gt; 테스트를 반복하는 작업</li>
<li>폭포수 개발론과 애자일 개발론은 각자 적용되는 프로젝트의 상황과 성격이 다를 수 있으며 반드시 한 개발론의 정답이 정해진 것은 아니다.</li>
<li>애자일 방법론은 고객의 피드백에 효과적으로 유연하고 대응할 수 잇지만, 반복적인 유지 보수 작업 발생과 번아웃 현상이 나타날 수 있다.</li>
<li>애자일 프레임 워크에는 MVP, Sprint, Scrum, TDdD, DevOps, CI / CD 등이 있으며 이들을 바탕으로 애자일 작업이 이루어진다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Docker와 Kubernetes]]></title>
            <link>https://velog.io/@hyeok_b2ng/Docker%EC%99%80-Kubernetes</link>
            <guid>https://velog.io/@hyeok_b2ng/Docker%EC%99%80-Kubernetes</guid>
            <pubDate>Fri, 05 Jul 2024 03:41:04 GMT</pubDate>
            <description><![CDATA[<h2 id="devops">DevOps</h2>
<p>어떤 방법이 있을까?
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/f09b20e8-fda9-47a4-860b-a4d835b1afd8/image.png" alt=""></p>
<p>DecOps : 철학과 방법론 =&gt; 문화</p>
<ul>
<li>자동화<ul>
<li>시간 절약</li>
<li>효율 증가</li>
</ul>
</li>
<li>측정<ul>
<li>빠른 분석</li>
<li>효율 증가</li>
</ul>
</li>
<li>공유<ul>
<li>팀원 지식 습득</li>
<li>효율 증가</li>
</ul>
</li>
<li>축적<ul>
<li>패시브 스킬</li>
<li>효율 증가</li>
</ul>
</li>
</ul>
<h3 id="서버-관리">서버 관리</h3>
<p>지나온 배경
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/3c19f708-10a2-4f90-9735-bf302cceba05/image.png" alt=""></p>
<p>Node.js로 서버를 설치하고 RUN하기위해서는?
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/1fbebaa5-ba80-40d5-be5a-ff9750ea8b81/image.png" alt="">
-&gt; 상당히 많은 시간이 든다.</p>
<h2 id="docker의-등장">Docker의 등장</h2>
<p>다양한 프로그램을 컨테이너로 사용할 수 있다.</p>
<p>Q. Docker는 VM인가?
A. 반은 맞지만 반은 틀리다. 가상머신처럼 독립적으로 실행되지만 빠르고, 쉽고, 효율적이다.</p>
<p>도커의 등장</p>
<ul>
<li>2013년 DotCloud에서 첫 공개</li>
<li>컨테이너 : 격리된 환경에서 작동하는 프로세스</li>
<li>리눅스 커널의 여러 기술을 활용</li>
<li>하드웨어 가상화 기술보다 가벼움</li>
<li>이미지 단위로 프로세스 실행 환경을 구성</li>
</ul>
<p>도커가 가져온 변화</p>
<ul>
<li>클라우드 이미지보다 관리하기 쉬움</li>
<li>다른 프로세스와 격리되어 가상머신처럼 사용하지만 성능저하가 거의 없음</li>
<li>복잡한 기술을 몰라도 사용할 수 있음</li>
<li>이미지 빌드 기록이 남음</li>
<li>코드와 설정으로 관리 &gt; 재현 및 수정 가능</li>
<li>오픈소스 ( 특정 회사 기술에 종속적이지 않음 )</li>
</ul>
<h3 id="컨테이너-오케스트레이션">컨테이너 오케스트레이션</h3>
<p>: 도커의 복잡한 컨테이너 환경을 효과적으로 관리하기 위한 도구</p>
<p>역할</p>
<ul>
<li>클러스터<ul>
<li>중앙제어 (master-node)</li>
<li>네트워킹</li>
<li>노드 스케일</li>
</ul>
</li>
<li>상태 관리</li>
<li>배포관리</li>
<li>ROLLOUT과 ROLLBACK</li>
<li>서비스 등록 및 조회</li>
<li>볼륨 스토리지</li>
</ul>
<h2 id="kubernetes">Kubernetes</h2>
<p>: 컨테이너를 쉽고 빠르게 배포/확장하고 관리를 자동화해주는 오픈소스 플랫폼</p>
<p><strong>쿠버네티스 소개</strong>
1주일에 20억개의 컨테이너를 생성하는 Google이 컨테이너 배포 시스템으로 사용하던 borg를 기반으로 만든 오픈소스</p>
<ul>
<li>운영환경에서 사용할 수 있다.</li>
<li>행성 스케일을 커버할 수 있다. (구글보다 적다면 ok)</li>
<li>유연성</li>
<li>어디서나 동작</li>
</ul>
<p>Q. Why kubernetes?
A. 오픈소스, 엄청난 인기, 무한한 확장성, 사실상의 표준</p>
<h3 id="gitops">GitOps</h3>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/72e34e6a-3575-4937-8401-8ef5d820d7ba/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Jira, JQL]]></title>
            <link>https://velog.io/@hyeok_b2ng/Jira-%ED%99%9C%EC%9A%A9</link>
            <guid>https://velog.io/@hyeok_b2ng/Jira-%ED%99%9C%EC%9A%A9</guid>
            <pubDate>Wed, 03 Jul 2024 08:10:31 GMT</pubDate>
            <description><![CDATA[<h2 id="지라란-">지라란 ?</h2>
<p>지라(Jira)
: 소프트웨어 개발 프로젝트를 위한 이슈 추적과 프로젝트 관리를 위한 플랫폼입니다. 이를 통해 개발자와 프로젝트 관리자는 각각의 역할에 맞게 일정, 작업 항목, 이슈 등을 관리할 수 있다.</p>
<p>왜 지라를 사용하는가?</p>
<ul>
<li>Issue Tracking and Project Management</li>
<li>Agile</li>
<li>DevOps</li>
<li>SRE<h3 id="issue-tracking">Issue Tracking</h3>
</li>
<li>버그, 요구 사항, 기능 개발 등의 이슈를 추적하고 관리</li>
<li>이슈에 대한 댓글, 첨부 파일을 추가 가능</li>
<li>이슈를 우선순위에 따라 분류하고, 상태를 확인
Issue Tracking Tools
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/baf8361a-1c67-4c0a-9f29-b95de6a1d77b/image.png" alt=""><h3 id="project-management">Project Management</h3>
</li>
<li>프로젝트 일정을 관리하고, 프로젝트 전반적인 진행 상황을 확인</li>
<li>프로젝트의 일부분을 분리하여 개별적으로 관리할 수 있는 하위 프로젝트를 생성</li>
<li>작업 항목을 생성하고, 담당자를 지정
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/8b754f20-8685-4074-8bd7-260cf1c55c36/image.png" alt=""><h3 id="agile">Agile</h3>
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/9e972b85-d565-4334-9c36-082f100992e6/image.png" alt=""><a href="https://agilemanifesto.org/iso/ko/manifesto.html">https://agilemanifesto.org/iso/ko/manifesto.html</a></li>
</ul>
<ol>
<li>스크럼 (Scrum)
프로젝트 관리를 위한 상호/점진적 개발 방법론으로 프로덕트 백로그, 스프린트 플래닝, 스프린트 백로그, 데일리 스크럼, 스프린트 리뷰, 프로덕트 릴리즈, 스프린트 회고의 과정을 일정한 주기로 반복하는 것을 의미하며, 주기가 완료될 때마다 고객에게 가치 있는 제품(소프트웨어)을 전달하는 방식이다.</li>
</ol>
<p>** 스크럼의 5가지 가치 **</p>
<ul>
<li>Courage(용기) : 자신이 옳은 일을 할 수 있도록 팀원 간 갈등과 도전을 통해 작업할 수 있는 용기</li>
<li>Commitment(합의) : 약속한 것을 확실히 실현</li>
<li>Foucus(초점) : 합의한 것의 실현에 전념</li>
<li>Respect(존중) : 자신과 다른 사람에게 경의를 표하는 것</li>
<li>Openness(개방성) : 어떤 것이 자신에게 불리해도 숨기지 않는 것<blockquote>
<p>스크럼은 점진적인 개발을 위한 기간(1~4주)을 설정한 후 해당 기간 동안 스크럼 팀 모두가 스크럼 절차를 함께 수행하여 고객에게 가치있는 증분된 제품을 인도하고, 스프린트를 반복 수행하여 최종적으로는 완성된 제품을 제공하는 것이다.</p>
</blockquote>
</li>
</ul>
<ol start="2">
<li>칸반
2-1. 칸반 방법론</li>
</ol>
<ul>
<li>프로세스의 연속적인 흐름을 유기적, 시각적으로 만들어 전체 프로세스를 유연하게 하는 방법론</li>
<li>제약이론의 당금 방식에서 착안</li>
<li>칸반은 카이젠(지속적 개선) 프로세서의 핵심</li>
<li>Work In Process을 통해 개발 프로세스에 병목현상이나 지나친 업무 쏠림을 방지</li>
</ul>
<p>2-2. 칸반 보드의 기본 형태</p>
<ul>
<li>칸반 보드는 기본적으로 계획, 진행 중, 완료의 형태를 사용<blockquote>
<p>칸반은 보드 형태의 업무 시각화 도구이며 우리가 진해앟는 모든 업무를 다 함께 공유하여 업무 가시성을 확보하고 투명한 업무 처리를 할 수 있게 하는데 목적이 있다. 프로젝트 지연을 예방할 수 있다.</p>
</blockquote>
</li>
</ul>
<h3 id="devops">DevOps</h3>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/16090d8b-6f54-4a1f-bcdf-cc231850def3/image.png" alt="">
이를 잘 수행하기 위해서 필요한 것들</p>
<ul>
<li>팀원 모두가 알고 있는 하나의 공유된 지표가 필요</li>
<li>장애나 이슈가 있을 때 팀과 공유 !</li>
<li>반복적인 작업들을 Tool을 이용해서 자동화</li>
<li>...
=&gt; Jira를 사용하면 모두 해결 !!
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/f92483ff-a5e0-485b-a326-3675733a36bb/image.png" alt="">
각 단계마다 이러한 Jira를 사용해서 관리해라</li>
</ul>
<h2 id="jql">JQL</h2>
<h3 id="jql이란">JQL이란</h3>
<p>SQL과 비슷한 문법이다.</p>
<ul>
<li>Jira Query Language</li>
<li>Jira Issue 를 구조적으로 검색하기 위해 제공하는 언어</li>
<li>각 필드에 맞는 특수한 예약어 들을 제공</li>
<li>쌓인 Issue들을 재가공해 유의미한 데이터를 도출해 내는데 활용<h3 id="operators">Operators</h3>
</li>
<li>=, !=, &gt;, &gt;= , ...</li>
<li>in, not in</li>
<li><del>(contains), !</del>(not contains)</li>
<li>is empty, is not empty, is null, is not null<h3 id="keywords">Keywords</h3>
</li>
<li>AND</li>
<li>OR</li>
<li>NOT</li>
<li>EMPTY</li>
<li>NULL</li>
<li>ORDER BY<h3 id="functions">Functions</h3>
</li>
<li>endOfDay(), startOfDay()</li>
<li>endOfWeek(), startOfWeek()</li>
<li>endOfMonth(), startOfMonth()</li>
<li>currentYear()<h3 id="jira만의-특성">Jira만의 특성</h3>
</li>
<li>날짜를 기준으로 다양한 것을 제공</li>
<li>1d, -2d, -3d, -1w, -2w, +1d, +2d, +1w, +2w, ...
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/435752f0-0bd1-425c-a7be-a08593b17d79/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Docker - 튜토리얼 1]]></title>
            <link>https://velog.io/@hyeok_b2ng/Docker-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC</link>
            <guid>https://velog.io/@hyeok_b2ng/Docker-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC</guid>
            <pubDate>Wed, 03 Jul 2024 06:00:08 GMT</pubDate>
            <description><![CDATA[<h2 id="컨테이너-실행">컨테이너 실행</h2>
<h3 id="docker의-개념-및-설치-확인">Docker의 개념 및 설치 확인</h3>
<p>Docker?
리눅스의 경량 가상화 기실인 리눅스 컨테이너 기능들을 쉽게 사용할 수 있도록 추상화 해둔 리눅스 명령어이자 서버 애플리케이션
기본적으로 리눅스 컨테이너는 리눅스 커널의 기능을 활용하기 때문에 리눅스를 필요로 하지만 Docker를 통해 윈도우에서도 심리스하게 활용할 수 있다.
Docker가 정상적으로 설치 및 실행되었다면 Client 버전과 Server 버전이 모두 출력될 것입니다. 정상적으로 동작중인지 확인하려면 <code>docker ps</code>를 실행해 보면 됩니다.<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/5d51d319-9102-49be-9fda-f0c50e45b69c/image.png" alt=""></p>
<h3 id="docker-시작하기">Docker 시작하기</h3>
<pre><code>$ docker run -it --rm rockylinux:9 bash</code></pre><p>처음 실행하면 대략 30초가량 시간이 걸립니다. 여기서 중요한 점은 셸 프롬프트가 다음과 같이 바뀐다는 점입니다.
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/ae60435b-9e14-412e-b48f-702a793d9db6/image.png" alt="">
여기서 rockylinux:9는 레드햇 엔프라이즈 리눅스 계열의 Rocky Linux 9버전을 의미합니다. 우분투와 비슷한 리눅스 배포판 중 하나입니다.</p>
<p><strong>docker run 명령어 이해하기</strong></p>
<ul>
<li>docker 명령어는 대부분 서브 커맨드로 구성
현재까지 docker run과 docker ps 명령어를 실행해보았습니다.
docker 명령어는 다음과 같은 형식으로 사용됩니다.<pre><code>docker &lt;SUBCOMMAND&gt; (&lt;OPTIONS&gt;)</code></pre>docker run 명령어의 형식은 다음과 같습니다.<pre><code>docker run (&lt;OPTIONS&gt;) &lt;IMAGENAME&gt; (&lt;COMMAND&gt;)</code></pre>그러면 실행하였던 <code>$ docker run -it --rm rockylinux:9 bash</code> 명령어는 어떤 명령어 일까요.
<code>-it</code> : 2개의 옵션으로 한 글자 옵션일 경우 -i, -t를 하나로 붙여서 사용이 가능합니다
<code>--rm</code> : 하나의 옵션
<code>rockylinux:9</code> : IMAGENAME
<code>bash</code> : COMMAND</li>
</ul>
<p>처음 실행했을 때 자세히 보면, Unable to find image라는 문구를 볼 수 있습니다. 즉, Docker는 로컬에 지정한 이미지가 있는지 확인한 다음, 없으면 Docker Hub라는 외부 이미지 저장소에서 이미지를 다운로드 받습니다. 도커에서는 전문 용어로 이미지를 풀 받는다(pull)고 표현합니다. 그리고 이 이미지를 기반으로 bash 명령어를 실행합니다. 또 전문 용어 하나 나옵니다. docker run으로 실행한 프로세스를 컨테이너라고 부릅니다.</p>
<p>그래서 같은 명령어를 두 번째 실행할 때는 이미지를 풀 받는 과정이 없으므로, 딜레이 없이 바로 컨테이너가 실행 됩니다. 풀 받는 시간이 없다면, 실행 시간은 거의 제로입니다.</p>
<h3 id="docker-컨테이너의-생애주기">Docker 컨테이너의 생애주기</h3>
<p> 컨테이너의 생애주기와 관련된 명령어도 잠깐 살펴보도록 하겠습니다. 컨테이너의 기본적인 생애주기는 “생성 -&gt; 실행 -&gt; 종료 -&gt; 삭제”입니다. 여기서는 생성과 실행은 하나로 묶어서 실행으로 두겠습니다. 그렇다면 종료와 삭제가 남습니다.
 셸 프롬프트에서 exit 명령어를 실행해서 셸을 종료할 수도 있지만, docker stop 명령어를 사용해서 컨테이너를 종료시킬 수도 있습니다.
<code>$ docker run -it debian:12 uname -a</code>으로 docker을 실행하면 어떻게 될까? <code>-rm</code> 옵션이 없으므로 즉시 종료되지 않는고 다음과 같이 남는 것을 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/948bde55-6541-43ab-bc42-1adf55092259/image.png" alt=""></p>
<p>이번에는 Exited 상태의 컨테이너가 보입니다. 바로 우리가 방금 실행한 컨테이너입니다. 이 컨테이너는 docker rm 명령어로 삭제할 수 있습니다.
<code>$ docker rm e35c3b</code></p>
<p> 예제를 위해 sleep 명령어로 컨테이너를 하나 실행해보겠습니다. 이 컨테이너는 1000초 후에 종료됩니다.</p>
<pre><code> $ docker run -it debian:12 sleep 1000</code></pre><p> docker rm에 -f 옵션을 붙여서 실행하면, 컨테이너가 실행중일 때 컨테이너를 강제 종료하고 삭제합니다.
 <code>$ docker rm -f 30740d</code>
 <img src="https://velog.velcdn.com/images/hyeok_b2ng/post/d859f53b-1ce8-46da-9102-1c78ec9d4171/image.png" alt=""></p>
<ul>
<li>docker run으로 컨테이너를 생성 및 실행하고, 명령어가 끝나면 컨테이너가 자동으로 종료됩니다.</li>
<li>docker stop 명령어로 컨테이너를 종료할 수 있습니다.</li>
<li>docker kill 명령어로 컨테이너를 강제 종료할 수 있습니다.</li>
<li>종료 상태의 컨테이너는 docker rm 명령어로 삭제할 수 있습니다.</li>
<li>컨테이너가 실행중이더라도 docker rm -f 명령어로 컨테이너를 강제 종료하고 삭제할 수 있습니다.</li>
</ul>
<blockquote>
<p>참고 : <a href="https://www.lainyzine.com/ko/article/docker-tutorial-1/">https://www.lainyzine.com/ko/article/docker-tutorial-1/</a></p>
</blockquote>
<h2 id="이미지와-도커-허브">이미지와 도커 허브</h2>
<h3 id="docker-이미지와-컨테이너">Docker 이미지와 컨테이너</h3>
<pre><code>$ docker run -it --rm rockylinux:9 uname -a</code></pre><p>위의 명령어에서 <code>rockylinux:9</code>은 이미지이다.
일반적으로 <code>uname -a</code>라는 명령어를 실행하면 현재 시스템에서 이 명령어를 찾아서 실행한다.
ex) Windows에는 uname이 없기 떄문에 에러가 난다.
하지만 맥 OS에서는 에러가 나지 않는다.
Docker이미지는 어떤 명령어가 실행되는 환경을 제공한다. 즉, <code>docker run -it --rm rockylinux:9 uname -a</code>이 명령어는 WSL 기반 윈도우 Docker Desktop에서 실행한 결과이다.
-&gt; 
분명 Windows에는 uname이라는 명령어가 없지만, Docker를 기반으로 uname 명령어를 실행할 수 있다. 앞에서는 docker run으로 실행한 프로세스를 컨테이너라고 부른다 했지만 좀 더 넓은 의미에서 보면 컨테이너는 어떤 명령어를 실행하는 환경과 그 위에서 실행한 프로세스까지 포함하는 개념이다.</p>
<p>Quiz.
만일 다음 명령어로 출력하면 어떤 결과가 나올까?</p>
<pre><code>$ docker run -it --rm rockylinux:9 bash -c &quot;rm /usr/bin/date; date&quot;</code></pre><p>date 명령어를 삭제하고 date 명령어를 실행한다. 여기서는 여러 명령어를 한 줄에서 실행하기 위해서 bash -c 명령어를 사용했고, 따옴표 사이에 여러 명령어들을 ; 세미콜론으로 구분지어 주면 여러 명령어를 실행할 수 있다. 결과는 명령어를 찾을 수 없다고 에러가 출력됩니다.</p>
<p>그러면 다음 명령어는 어떻게 동작할까?</p>
<pre><code>$ docker run -it --rm rockylinux:9 date</code></pre><p>위 명령어는 정상적으로 동작한다. 즉 컨테이너는 상태를 공유하지 않고 어떤 컨테이너에서 파일을 삭제하더라도 다른 컨테이너에는 영향을 끼치지 않는다. 이를 <strong>이식성</strong> 이라고 한다.</p>
<blockquote>
<p>+) 컨테이너를 종료된 상태로 남겨놓았을 때 지우기 위해서는 docker rm명령어로 모두 지워야한다. 하지만 <code>docker container prune</code>명령어를 사용한다면 종료된 컨테이너를 일괄 삭제할 수 있다.</p>
</blockquote>
<h3 id="docker-이미지와-docker-hub">Docker 이미지와 Docker Hub</h3>
<pre><code>$ docker images
REPOSITORY               TAG                       IMAGE ID       CREATED         SIZE
nginx                    latest                    021283c8eb95   6 days ago      187MB
debian                   12                        3676c78a12ad   6 days ago      116MB
rockylinux               9                         eeea865f4111   6 weeks ago     176MB</code></pre><p><code>docker image ls</code>와 같은 명령어임. 둘 다 자주 사용된다.</p>
<blockquote>
<p><strong>Docker와 유니온 마운트</strong>
컨테이너들은 이미지를 공유해서 사용하기 때문에, 컨테이너를 실행한다고 용량이 추가로 필요하지는 않습니다. 대신 컨테이너들은 컨테이너들에서 사용하는 고유한 디스크를 가지고 있습니다. 컨테이너는 이 디스크에 사용하는 만큼만 추가적으로 용량을 차지합니다. </p>
</blockquote>
<p>이미지는 디스크 용량을 차지한다. 인터넷에 연결되어 있으면 언제든 다운로드 받을 수 있기 때문에 로컬에 꼭 필요하지 않은 이미지를 미리 다운로드 받아놓을 필요는 없다. 이미지를 삭제할 때는 docker rmi 혹은 docker image rm을 사용하고, 이미지를 삭제하기 전에 이미지를 사용하는 모든 컨테이너를 종료해야한다.
-&gt; rmi 명령어나 -f 옵션으로 이미지가 사용중이라도 삭제할 수 있지만 이는 강제 삭제가 아니라 이미지의 이름만 지울 뿐이다.</p>
<blockquote>
<p>출처
<a href="https://www.lainyzine.com/ko/article/docker-tutorial-2/#table-of-contents">https://www.lainyzine.com/ko/article/docker-tutorial-2/#table-of-contents</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA 어노테이션 정리]]></title>
            <link>https://velog.io/@hyeok_b2ng/JPA-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@hyeok_b2ng/JPA-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 01 Jul 2024 13:26:22 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>JPA를 공부, 사용하면서 생기는 어노테이션에 대해 정리한 포스트입니다.
따라서, 수정이 계속 이루어질 수 있는 포스트입니다.</p>
</blockquote>
<h2 id="매핑과-관련된-어노테이션">매핑과 관련된 어노테이션</h2>
<h3 id="entity">@Entity</h3>
<p> JPA를 사용해 테이블과 일대일 매핑할 클래스에 붙여주는 어노테이션이다. 이 어노테이션을 붙임으로써 JPA가 해당 클래스를 관리하게 된다.
 SpringBoot를 설정할 시 spring.jpa.hibernate.ddl-auto 설정이 create or update로 설정할 경우 프로젝트 시작 시 EntityManager가 자동으로 DDL을 수행해준다.</p>
<table>
<thead>
<tr>
<th align="left"><center>속성</center></th>
<th align="left"><center>기능</center></th>
<th align="left"><center>기본값</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left">name</td>
<td align="left">JPA에서 사용할 엔티티 이름 지정</td>
<td align="left">클래스 이름을 엔티티 이름으로 지정</td>
</tr>
<tr>
<td align="left">### @Table</td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="left">엔티티와 매핑할 테이블을 지정해주는 어노테이션이다.</td>
<td align="left"></td>
<td align="left"></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="left"><center>속성</center></th>
<th align="left"><center>기능</center></th>
<th align="left"><center>기본값</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left">name</td>
<td align="left">매핑할 테이블 이름</td>
<td align="left">엔티티 이름(@Entity(name=&quot;~&quot;) 사용</td>
</tr>
<tr>
<td align="left">uniqueContraints</td>
<td align="left">DDL 생성시 유니크 제약조건 생성</td>
<td align="left"></td>
</tr>
<tr>
<td align="left">### @Column</td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="left">객체 필드를 테이블 컬럼과 매핑한다.</td>
<td align="left"></td>
<td align="left"></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="left"><center>속성</center></th>
<th align="left"><center>기능</center></th>
<th align="left"><center>기본값</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left">name</td>
<td align="left">필드와 매핑할 테이블의 컬럼 이름 지정</td>
<td align="left">필드이름으로 대체</td>
</tr>
<tr>
<td align="left">insertable</td>
<td align="left">Entity 저장 시 해당 Field도 저장, false로 읽기 전용 설정 가능</td>
<td align="left">true</td>
</tr>
<tr>
<td align="left">updatable</td>
<td align="left">Entity 수정 시 해당 Field도 수정, false로 읽기 전용 설정 가능</td>
<td align="left">true</td>
</tr>
<tr>
<td align="left">table</td>
<td align="left">하나의 엔티티를 두 개 이상의 테이블에 매핑할 때 사용</td>
<td align="left">현재 Class가 매핑된 Table</td>
</tr>
<tr>
<td align="left">nullable</td>
<td align="left">true/false로 null 허용 여부 설정</td>
<td align="left">true</td>
</tr>
<tr>
<td align="left">unique</td>
<td align="left">컬럼에 유니크 제약조건 부여</td>
<td align="left">true</td>
</tr>
<tr>
<td align="left">length</td>
<td align="left">문자 길이 제약조건 String 타입일 때 사용</td>
<td align="left">255</td>
</tr>
<tr>
<td align="left">columnDefinition</td>
<td align="left">데이터베이스 컬럼 정보를 직접 부여</td>
<td align="left">적절한 Column Type 생성</td>
</tr>
<tr>
<td align="left">precision, scale</td>
<td align="left">BigDecimal 타입에서 사용, precision : 소수점을 포함한 전체 자릿수 설정, scale : 소수의 자릿수</td>
<td align="left"></td>
</tr>
</tbody></table>
<p>-&gt; 여기서 name, insertable, updatable, table을 제외한 나머지 속성들은 DDL 생성 기능을 사용할 때만 사용되는 속성들이다. 즉 실행 로직에는 영향을 끼치지 않는다.</p>
<h3 id="id">@Id</h3>
<p>특정 속성을 PK로 설정하는 어노테이션이다.</p>
<ul>
<li><code>@Id</code> 어노테이션만 적게될 경우 기본키값을 직접 부여해줘야 한다.</li>
</ul>
<p><code>@GeneratedValue</code> 어노테이션을 사용하면 기본값을 DB에서 자동으로 생성하는 전략을 사용 할 수 있다.</p>
<p>전략에는 IDENEITY, SEQUENCE, TABLE 3가지가 있다.</p>
<ul>
<li><p>IDENTITY : 기본 키 생성을 데이터베이스에 위임한다.(ex MySQL - AUTO INCREMENT...)</p>
</li>
<li><p>SEQUENCE : 데이터베이스 시퀀스를 사용해서 기본 키를 할당한다.(ex Oracle sequence...)</p>
</li>
<li><p>TABLE : 키 생성 테이블을 사용한다.(ex 시퀀스용 테이블을 생성해서 테이블의 기본키를 저장하고 관리한다.)</p>
</li>
</ul>
<p>※ <code>@GeneratedValue(startegy = GenerationType.AUTO)</code> 인 경우 선택된 DB에 따라 JPA가 자동으로 전략 선택</p>
<h3 id="embeddedid">@EmbeddedId</h3>
<p>2개 이상의 키를 가지는 복합키로 테이블을 매칭하는 경우 사용하는 어노테이션이다.</p>
<ul>
<li><code>@EmbeddedId</code> 어노테이션을 붙여줘야 한다.</li>
<li>Serializable 인터페이스를 구현해야 한다.</li>
<li>기본 생성자가 있어야 한다.</li>
<li>식별자 클래스는 public 이어야 한다.<h3 id="enumerated">@Enumerated</h3>
enum 타입을 매핑할 때 사용한다.
속성 value</li>
<li><code>EnumType.ORDINAL</code> : enum 순서를 DB에 저장 </li>
<li><code>EnumType.STRING</code> : enum이름을 DB에 저장</li>
</ul>
<p>⭐️ 대부분 경우 STRING을 무조건 사용하는 것이 오류를 줄일 수 있다.
Why? 만약 2개를 저장해둔 enum이 있다고 가정하자. 이 경우를 ORDINAL 속성으로 두면 추후 enum에 1개를 추가할 경우 순서가 밀려 DB의 값들이 깨져서 심각한 오류를 발생할 가능성이 높다. 따라서 STRING으로 저장해두는 것이 안전하다.</p>
<h3 id="transient">@Transient</h3>
<p> 어노테이션을 붙인 필드는 DB에 저장하지도 조회하지도 않는다. 객체에 임시로 값을 보관하고 싶을 때 사용</p>
<h2 id="연관-관계와-관련된-어노테이션">연관 관계와 관련된 어노테이션</h2>
<h3 id="관계-에서-사용하는-어노테이션">관계 에서 사용하는 어노테이션</h3>
<p><code>@JoinColumn(name=&quot;&quot;)</code>
외래키를 정의하는 어노테이션이다.</p>
<table>
<thead>
<tr>
<th align="left"><center>속성</center></th>
<th align="left"><center>기능</center></th>
<th align="left"><center>기본값</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left">name</td>
<td align="left">매핑할 외래키 이름</td>
<td align="left">&#39;필드명&#39;_&#39;참조하는 테이블 기본키 컬럼명&#39;</td>
</tr>
<tr>
<td align="left">referencedColumnName</td>
<td align="left">외래키가 참조하는 대상 테이블의 컬럼명</td>
<td align="left">참조하는 테이블 기본키 컬럼명</td>
</tr>
<tr>
<td align="left">foreignKey</td>
<td align="left">외래 키 제약조건 지정</td>
<td align="left"></td>
</tr>
</tbody></table>
<p><code>@XXToXX</code>
N:1 관계를 표현하는 어노테이션이다. @ManyToOne이 붙은 엔티티가 M이고 반대 엔티티가 1일 때 붙인다.</p>
<table>
<thead>
<tr>
<th align="left"><center>속성</center></th>
<th align="left"><center>기능</center></th>
<th align="left"><center>기본값</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left">optional</td>
<td align="left">false : 연관된 엔티티가 항상 존재</td>
<td align="left">true</td>
</tr>
<tr>
<td align="left">fetch</td>
<td align="left">글로벌 페치 전략 설정</td>
<td align="left">@XXToOne=FetchType.EAGER @XXToMany=FetchType.LAZY</td>
</tr>
<tr>
<td align="left">cascade</td>
<td align="left">영속성 전이 기능 사용</td>
<td align="left"></td>
</tr>
<tr>
<td align="left">targetEntity</td>
<td align="left">연관된 엔티티 타입 정보 설정</td>
<td align="left"></td>
</tr>
</tbody></table>
<h3 id="양방향-관계">양방향 관계</h3>
<p>속성으로 mappedBy가 사용, mapppedBy는 양방향 매핑일 때 사용한다.
속성 값에는 반대쪽 매핑의 필드 이름값을 넣는다.
-&gt; 반대쪽 클래스 명으로 매핑 필드값을 정하면 될텐데 왜 mappedBy를 이용해야하는 것인가?
∵ 객체는 서로 다른 단방향 2개로 이루어져있는것과 관련이 있다.
그러면, 2개의 연관관계 ID가 필요하게 된다. 즉, 외래키 2개를 관리하게 될텐데 관계형 데이터베이스는 외래키 1개를 가지고 관리한다.
그래서 JPA는 두 개의 연관관계중 하나를 고르게 하기 위해 mappedBy를 설정한다. 여기서 관리되는 연관관계를 연관관계의 주인이라 한다. 다시 말해 mappedBy가 없는 엔티티가 연관관계의 주인이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA 강의 2 - 도메인 분석 설계]]></title>
            <link>https://velog.io/@hyeok_b2ng/JPA-%EA%B0%95%EC%9D%98-2-%EB%8F%84%EB%A9%94%EC%9D%B8-%EB%B6%84%EC%84%9D-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@hyeok_b2ng/JPA-%EA%B0%95%EC%9D%98-2-%EB%8F%84%EB%A9%94%EC%9D%B8-%EB%B6%84%EC%84%9D-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Mon, 01 Jul 2024 10:25:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 게시글의 출처는 인프런 김영한님의 강의입니다.
강의명은 실전! 스프링 부트와 JPA 활용 1 - 웹 애플리케이션 개발입니다.
출처 : <a href="https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-JPA-%ED%99%9C%EC%9A%A9-1">https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-JPA-%ED%99%9C%EC%9A%A9-1</a></p>
</blockquote>
<h2 id="요구사항-분석">요구사항 분석</h2>
<ul>
<li>회원 기능<ul>
<li>회원 등록</li>
<li>회원 조회</li>
</ul>
</li>
<li>상품 기능<ul>
<li>상품 등록</li>
<li>상품 수정</li>
<li>상품 조회</li>
</ul>
</li>
<li>주문 기능<ul>
<li>상품 주문</li>
<li>주문 내역 조회</li>
<li>주문 취소</li>
</ul>
</li>
<li>기타 요구사항<ul>
<li>상품은 재고 관리가 필요하다.</li>
<li>상품의 종류는 도서, 음반, 영화가 있다.</li>
<li>상품을 카테고리로 구분할 수 있다.</li>
<li>상품 주문시 배송 정보를 입력할 수 있다.</li>
</ul>
</li>
</ul>
<h2 id="도메인-모델과-테이블-설계">도메인 모델과 테이블 설계</h2>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/e6ac770f-0c40-4c9a-8749-7bfab49c4591/image.png" alt=""></p>
<h3 id="회원-엔티티-분석">회원 엔티티 분석</h3>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/5e62b9b7-3437-4bce-bd3a-d773cfe70acb/image.png" alt=""></p>
<p>※ 회원이 주문을 하기 때문에, 회원이 주문리스트를 가지는 것은 잘 설계한 것처럼 보이지만 객체의 관점와 실제 세상의 관점은 다르다.
회원이 주문을 참조할 필요가 없고, 주문이 회원을 참조하는 것으로 충분하다. 여기서는 다양한 예제를 위해 추가했으므로 실무에서는 ordes를 List로 표현한 것은 쓸모없다.</p>
<h3 id="회원-테이블-분석">회원 테이블 분석</h3>
<p><img src="https://velog.velcdn.com/images/hyeok_b2ng/post/38811e95-8ec0-4fba-84fb-463735e2bbe2/image.png" alt="">
다대다는 사용하지 않아야한다. 여기서 카테고리와 아이템의 사이에 category_item을 두어 다대일 관계로 풀어냈다.</p>
<h3 id="연관관계-매핑-분석">연관관계 매핑 분석</h3>
<p>회원과 주문 : 외래키가 있는 주문을 연관관계의 주인으로 정하는 것이 좋다.
주문상품과 주문 : 다대일 양방향 관계다. 외래 키가 주문상품에 있으므로 주문상품이 연관관계의 주인이다. 그러므로 <code>OrderItem.order</code> 를 <code>ORDER_ITEM.ORDER_ID</code> 외래 키와 매핑한다.</p>
<blockquote>
<p>※ <strong>외래키가 있는 곳을 연관관계의 주인으로 정해라.</strong>
연관관계의 주인은 단순히 외랰 키를 누가 관리하냐의 문제이지 비즈니스상 우위에 있다고 주인으로 정하면 안된다. 예를 들어 자동차와 바퀴가 잇으면, 일대다 관계에서 항상 다쪽에 외래키가 있으므로 외래키가 있는 바퀴를 연관관계의 주인으로 정하면 된다. 물론 자동차를 연관관계의 주인으로 정하는 것이 불가능 한 것은 아니지만, 자동차를 연관관계의 주인으로 정하면 자동차를 관리하지 않는 바퀴 테이블의 외래키 값이 업데이트 되므로 관리와 유지보수가 어렵고, 추가적으로 별도의 업데이트 쿼리가 발생하는 성능 문제도 있다.</p>
</blockquote>
<h2 id="엔터티-클래스-개발">엔터티 클래스 개발</h2>
<p>이론적으로 Getter, Setter 모두 제공하지 않고, 꼭 필요한 별도의 메서드를 제공하는게 가장 이상적이다. 하지만 실무에서 엔티티의 데이터는 조회할 일이 너무 많으므로 Getter의 경우 열어두는 것이 편리하다. but, setter는 호출하면 데이터가 변한다. 따라서 엔티티를 변경할 때는 Setter 대신에 변경 지점이 명확하도록 변경을 위한 비즈니스 메서드를 별도로 제공해야한다.</p>
<p>※ 실무에서는 <code>@ManyToMany</code> 를 사용하지 말자
<code>@ManyToMany</code> 는 편리한 것 같지만, 중간 테이블( <code>CATEGORY_ITEM</code> )에 컬럼을 추가할 수 없고, 세밀하게 쿼리를 실행하기 어렵기 때문에 실무에서 사용하기에는 한계가 있다. 중간 엔티티( <code>CategoryItem</code> 를 만들고 <code>@ManyToOne</code> , <code>@OneToMany</code> 로 매핑해서 사용하자. 정리하면 다대다 매핑을 일대다, 다대일 매핑으로 풀어내서 사용하자.</p>
<p><strong>값 타입은 변경 불가능하게 설계해야 한다.</strong>
@Setter를 제거하고, 생성자에서 값을 모두 초기화해서 변경 불가능한 클래스를 만들자. JPA 스펙상 엔티티나 임베디드 타입 (<code>@Embeddable</code>)은 자바 기본 생성자를 public 또는 protected로 설정해야한다. public보다는 protected가 그나마 더 안전하다.
JPA가 이런 제약을 두는 이유는 JPA 구현 라이브러리가 객체를 생성할 떼 리플랙션 같은 기술을 사용할 수 있도록 지원해야 하기 때문이다.</p>
<h2 id="엔터티-설계시-주의점">엔터티 설계시 주의점</h2>
<p><strong>엔터티에는 가급적 Setter를 사용하지 말자</strong></p>
<ul>
<li>Setter가 모두 열려있으면 변경 포인트가 너무 많아서, 유지보수가 어렵다.</li>
</ul>
<p><strong>모든 연관관계는 지연 로딩으로 설정 !</strong></p>
<ul>
<li>즉시 로딩(EAGER)은 예측이 어렵고, 어떤 SQL이 실행될지 추적하기 어렵다. 특히 JPQL을 실핼할 때 N+1 문제가 자주 발생한다.</li>
<li>실무에서는 모든 연관관계는 지연로딩(LAZY)으로 설정해야 한다.</li>
<li>연관된 엔터티를 함께 DB에서 조회해야 하면, fetch join 또는 엔티티 그래프 기능을 사용한다.</li>
<li>@XToOne(OneToOne, ManyToOne) 관계는 기본이 즉시 로딩이므로 직접 지연로딩으로 설정해야 한다.<ul>
<li>OneToMany는 기본값이 지연로딩이다.</li>
</ul>
</li>
</ul>
<p><strong>컬렉션은 필드에서 초기화 하자</strong>
 컬렉션은 필드에서 바로 초기화 하는 것이 안전하다.</p>
<ul>
<li>null 문제에서 안전하다.</li>
<li>하이버네이트는 엔티티를 영속화 할 때, 컬렉션을 감싸서 하이버네이트가 제공하는 내장 컬렉션으로 변경한다. 만약 getOrders() 처럼 임의의 메서드에서 컬렉션을 잘못 생성하면 하이버네이트 내부 메커니즘에 문제가 발생할 수 있다. 따라서 필드레벨에서 생성하는 것이 가장 안전하고, 코드도 간결하다.</li>
</ul>
<p><strong>테이블, 컬럼명 생성 전략</strong>
스프링 부트에서 하이버네이트 기본 매핑 전략을 변경해서 실제 테이블 필드명은 다름</p>
<p>하이버 네이트 기존 구현 : SpringPhysicalNamingStrategy</p>
<p>스프링 부트 신규 설정 (엔티티(필드) -&gt; 테이블(칼럼))</p>
<ol>
<li>카멜 케이스 -&gt; 언더스코어</li>
<li>. -&gt; _</li>
<li>대문자 -&gt; 소문자</li>
</ol>
<p>적용 2 단계
논리명 생성 : 명시적으로 컬럼, 테이블명을 직접 적지 않으면 ImplicitNamingStrategy 사용</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA 강의 - 프로젝트 환경설정]]></title>
            <link>https://velog.io/@hyeok_b2ng/JPA-%EA%B0%95%EC%9D%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@hyeok_b2ng/JPA-%EA%B0%95%EC%9D%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95</guid>
            <pubDate>Sun, 30 Jun 2024 10:23:44 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 게시글의 출처는 인프런 김영한님의 강의입니다.
강의명은 실전! 스프링 부트와 JPA 활용 1 - 웹 애플리케이션 개발입니다.
출처 : <a href="https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-JPA-%ED%99%9C%EC%9A%A9-1">https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-JPA-%ED%99%9C%EC%9A%A9-1</a></p>
</blockquote>
<h2 id="프로젝트-환경설정">프로젝트 환경설정</h2>
<p>프로젝트 생성</p>
<ul>
<li>스프링 부트 스타터(<a href="https://start.spring.io/">https://start.spring.io/</a>)</li>
<li>spring web, hymeleaf, jpa, h2, lombok, validation<h2 id="라이브러리-살펴보기">라이브러리 살펴보기</h2>
</li>
<li>핵심 라이브러리<ul>
<li>스프링 MVC</li>
<li>스프링 ORM</li>
<li>JPA, 하이버네이트</li>
<li>스프링 데이터 JPA</li>
</ul>
</li>
<li>기타 라이브러리<ul>
<li>H2 데이터베이스 클라이언트</li>
<li>커넥션 풀 : 부트 기본은 HikariCP</li>
<li>WEB(thymeleaf)</li>
<li>로깅 slf4j &amp; LogBack</li>
<li>테스트
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/8c19a290-9b95-4a40-afee-501f08649d8c/image.png" alt=""></li>
</ul>
</li>
</ul>
<h3 id="추가-학습-1---connection-pool">추가 학습 1 - connection pool</h3>
<p>데이터베이스와 연결하기 위해 Connection 객체를 생성하는 작업은 비용이 굉장히 많이 드는 작업 중 하나이다.</p>
<p>Connection 객체를 생성하는 과정
① 애플리케이션에서 DB 드라이버를 통해 커넥션을 조회
② DB 드라이버는 DB와 TCP/IP 커넥션을 연결한다.
③ DB 드라이버는 TCP/IP 커넥션이 연결되면 아이디와 패스워드, 기타 부가 정보를 DB에 전달
④ DB는 아이디, 패스워드를 통해 내부 인증을 거친 후 내부에 DB를 생성
⑤ DB는 커넥션 생성이 완료되었다는 응답을 보낸다.
⑥ DB 드라이버는 커넥션 객체를 생성해서 클라이언트에 반환한다.
→ DB 연결할 때마다 Connection 객체를 새로 만드는 것은 비용이 많이 들며, 굉장히 비효율적이다.</p>
<p>이러한 과정을 계속 반복하지 않고, 애플리케이션 로딩 시점에 객체를 미리 생성해두고 연결 요청이 들어올 시 준비된 객체를 사용하여 성능을 향상시키는 방법이다. 쉬운 말로 connetion을 어떤 장소에 만들어두고 꺼내쓴다고 이해하면 편한 것 같다.</p>
<p>※ Spring Boot 2.0 이전 버전에서는 Apche 재단의 오픈 소스인 Apache Commons DBCP를 주로 사용하였지만, Spring Boot 2.0 이후에는 HikariCP를 기본 DBCP로 채택하여 사용되고 있다. 아래 그림을 참고하면 Hikari의 성능이 우수한 것을 알 수 있다.
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/ed81557b-f3f5-43ea-af21-072a6e6d06ea/image.png" alt=""></p>
<p><strong>주의!</strong> <code>application.yml</code> 같은 <code>yml</code> 파일은 띄어쓰기(스페이스) 2칸으로 계층을 만듭니다. 따라서 띄어쓰기 2칸을 필수로 적어주어야 합니다.
예를 들어서 아래의 <code>datasource</code> 는 <code>spring:</code> 하위에 있고 앞에 띄어쓰기 2칸이 있으므로
<code>spring.datasource</code> 가 됩니다. 다음 코드에 주석으로 띄어쓰기를 적어두었습니다.</p>
<h3 id="쿼리-파라미터-로그-남기기">쿼리 파라미터 로그 남기기</h3>
<ul>
<li>로그에 org.hibernate.orm.jdbc.bind: trace 추가하기</li>
<li>외부 라이브러리 사용<ul>
<li><a href="https://github.com/gavlyukovskiy/spring-boot-data-source-decorator">https://github.com/gavlyukovskiy/spring-boot-data-source-decorator</a></li>
<li>위 라이브러리를 사용하면 쿼리문의 value값까지 볼 수 있다는 장점이 있음.
but, 성능저하의 우려가 있으니 개발과정에서만 사용하고 배포시 사용하지 않는 것을 추천</li>
<li>스프링 부트 3.0 이상을 사용하면 라이브러리 버전을 1.9.0 이상을 사용해야 한다.<pre><code class="language-java">implementation &#39;com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.9.0&#39;</code></pre>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HSAT 4회 정기 코딩 인증평가 기출] 슈퍼컴퓨터 클러스터 with Java]]></title>
            <link>https://velog.io/@hyeok_b2ng/HSAT-4%ED%9A%8C-%EC%A0%95%EA%B8%B0-%EC%BD%94%EB%94%A9-%EC%9D%B8%EC%A6%9D%ED%8F%89%EA%B0%80-%EA%B8%B0%EC%B6%9C-%EC%8A%88%ED%8D%BC%EC%BB%B4%ED%93%A8%ED%84%B0-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-with-Java</link>
            <guid>https://velog.io/@hyeok_b2ng/HSAT-4%ED%9A%8C-%EC%A0%95%EA%B8%B0-%EC%BD%94%EB%94%A9-%EC%9D%B8%EC%A6%9D%ED%8F%89%EA%B0%80-%EA%B8%B0%EC%B6%9C-%EC%8A%88%ED%8D%BC%EC%BB%B4%ED%93%A8%ED%84%B0-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-with-Java</guid>
            <pubDate>Fri, 28 Jun 2024 08:29:11 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>문제 출처 : <a href="https://softeer.ai/practice/6252">https://softeer.ai/practice/6252</a></p>
</blockquote>
<p>대규모 머신 러닝에서는 여러 컴퓨터를 하나의 클러스터로 묶어 계산을 수행하는 경우가 많다. 병렬 컴퓨팅 파워가 늘어나면 훨씬 더 거대한 데이터도 실용적으로 사용할 수 있게 된다. 클라우드 컴퓨팅을 이용하는 기업도 많지만, 개인정보와 보안, 네트워킹, 비용 등의 문제로 직접 클러스터를 구축하는 경우도 많다.</p>
<p>현지도 이러한 머신 러닝용 클러스터를 관리하는 역할을 맡고 있다. 클러스터의 성능은 컴퓨터의 수가 많아질 수록, 각각의 성능이 올라갈 수록 향상된다. 그런데 어느 날 협업 중인 몇몇 연구실에서 클러스터의 성능을 업그레이드해 달라는 요청을 보내 왔다. 이들은 특히 클러스터를 이루는 각각의 컴퓨터 중 성능이 가장 낮은 컴퓨터의 성능이 병목이 된다고 알려 왔다.</p>
<p>이 클러스터에는 N대의 컴퓨터가 있으며, 각각의 성능은 ai라는 정수로 평가할 수 있다. 현지는 각각의 컴퓨터에 비용을 들여 업그레이드를 진행할 수 있다. 성능을 d만큼 향상시키는 데에 드는 비용은 d2원이다. (단, d는 자연수이다.)</p>
<p>업그레이드를 하지 않는 컴퓨터가 있어도 되지만, 한 컴퓨터에 두 번 이상 업그레이드를 수행할 수는 없다.</p>
<p>업그레이드를 위한 예산이 B원 책정되어 있다. 현지의 목표는 B원 이하의 총 비용으로 업그레이드를 수행하여, 성능이 가장 낮은 컴퓨터의 성능을 최대화하는 것이 목표이다. 이 최선의 최저성능을 계산하는 프로그램을 작성하시오.</p>
<blockquote>
<p>제약조건
1≤N≤10^5인 정수
1≤ai≤10^9인 정수
1≤B≤10^18인 정수</p>
</blockquote>
<p>B 값이 매우 크므로 long으로 선언함에 유의하자.</p>
<h2 id="풀이">풀이</h2>
<h3 id="오답-리마인드">오답 리마인드</h3>
<p>N이 10만에 B가 매우 크므로 문제에서 주어진 것 처럼 하나씩 탐색하는 것은 무조건 시간초과로 계산하였다.</p>
<h3 id="1번째-풀이">1번째 풀이</h3>
<ol>
<li>PriorityQueue에 입력값들을 넣기 (O(NlogN))</li>
<li>pq에서 하나씩 꺼내면서 최저 값과 비교 후 cost 계산. ArrayList에 지나온 pq의 값을 넣어주면서 다음 최저 값 비교 시 해당 list 탐색 후 pq로 적은 수를 탐색할 수 있도록 함.<pre><code class="language-java">import java.io.*;
import java.util.*;
</code></pre>
</li>
</ol>
<p>public class Main {</p>
<pre><code>public static void main(String[] args) throws Exception{
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    StringTokenizer st = new StringTokenizer(br.readLine());
    int N = Integer.parseInt(st.nextToken());
    long B = Long.parseLong(st.nextToken());
    PriorityQueue&lt;Integer&gt; pq = new PriorityQueue&lt;&gt;();
    st = new StringTokenizer(br.readLine());
    int minPerformance = Integer.MAX_VALUE;
    for (int i = 0; i &lt; N; i++) {
        int ai = Integer.parseInt(st.nextToken());
        minPerformance = Math.min(minPerformance, ai);
        pq.offer(ai);
    }

    boolean flag = true;
    ArrayList&lt;Integer&gt; preList = new ArrayList&lt;&gt;();
    while (flag) {
        minPerformance++;
        long cost = 0;
        for (int i = 0; i &lt; preList.size(); i++) {
            int preN = preList.get(i);
            cost += Math.pow(minPerformance - preN, 2);
            if (cost &gt; B) {
                flag = false;
                break;
            }
        }
        if (!flag) break;
        while (!pq.isEmpty() &amp;&amp; pq.peek() &lt; minPerformance) {
            int num = pq.poll();
            cost += Math.pow(minPerformance - num, 2);

            if (cost &gt; B) {
                flag = false;
                break;
            }
            preList.add(num);
        }
    }

    System.out.print(minPerformance-1);
    br.close();
}</code></pre><p>}</p>
<pre><code>### 결과
subtask1, 2에서는 정답으로 처리되었지만 subtask3에서 대부분 시간초과가 발생했다.
-&gt; 음, 최저 값(정답)을 서칭할 때 +1씩하는 것이 아닌 다음 최저 값으로 갱신하면서 cost로 해당 최저 값이 불가할 시 두 인덱스 사이를 탐색한다면 시간을 줄일 수 있지 않을까 ? 생각하였다.
![](https://velog.velcdn.com/images/hyeok_b2ng/post/1ed9c44e-f80d-498c-9319-e7533b458236/image.png)
### 2번째 풀이
1) 풀이에서 얻은 방법대로 가장 정답에 근접한 인덱스를 찾고 해당 인덱스 사이를 +1로 탐색하며, 찾자.
-&gt; 설계과정에서 최댓값을 설정하면 서칭이 빠를 듯 했지만 최댓값 설정이 뚜렷하게 생각나지 않아 설계로 들어감
```java
import java.io.*;
import java.util.*;

public class Main {

    public static void main(String[] args) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int N = Integer.parseInt(st.nextToken());
        long B = Long.parseLong(st.nextToken());
        int[] performance = new int[N];
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i &lt; N; i++) performance[i] = Integer.parseInt(st.nextToken());
        Arrays.sort(performance);
        int minPer = performance[0];
        int minIdx = 0;
        boolean find = false;
        // 다음 인표치로 잡고 가능성 계산
        while (true) {
            minIdx++;
            if (minIdx == N) break;
            minPer = performance[minIdx];
            long cost = 0;
            for (int i = 0; i &lt; minIdx; i++) {
                cost += Math.pow(minPer - performance[i], 2);
                if (cost &gt; B) {
                    find = true;
                    break;
                }
            }
            if (find) break;
        }
        // 찾은 인덱스의 -1부터 값을 키우면서 가능한 최저 성능 찾기
        minPer = performance[minIdx-1];
        boolean flag = true;
        while (flag) {
            minPer++;
            long cost = 0;
            for (int i = 0; i &lt; minIdx; i++) {
                cost += Math.pow(minPer - performance[i], 2);
                if (cost &gt; B) {
                    flag = false;
                    break;
                }
            }
        }
        System.out.println(minPer-1);
        br.close();
    }
}</code></pre><h3 id="결과">결과</h3>
<p>subtask1, 2는 정답. 풀이 1보다는 subtask3에서 시간을 많이 줄였으나 역시 두 인덱스 사이의 값 차이가 크다면 시간복잡도를 해결하지 못하는 문제를 직면
-&gt; 음... 빠르게 값을 찾는다 ? 이진탐색. 최대값 설정이 가능한가를 다시 고민해봄. 10^9의 제곱이 10^18이니깐 최대로 증가할 수 있는 값은  10^9. 따라서 최대 성능은 2*10^9.
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/fbbcbb98-2a01-45f1-9bf6-76041f9f9d83/image.png" alt=""></p>
<h3 id="3번째-풀이-정답">3번째 풀이 (정답)</h3>
<p>최저값 1
최대값 2 * 10^9
-&gt; 이진탐색, 가능여부판단하며 정답값 도출</p>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main {
    static int[] performance;
    static int N;
    static long B;
    public static void main(String[] args) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        B = Long.parseLong(st.nextToken());
        performance = new int[N];
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i &lt; N; i++) performance[i] = Integer.parseInt(st.nextToken());
        Arrays.sort(performance);
        long low = 1;
        long high = 2 * 1_000_000_000;
        while (low &lt; high) {
            long mid = (low + high+1) / 2;
            if (sol(mid)) low = mid;
            else high = mid - 1;
        }
        System.out.print(low);
        br.close();
    }
    static boolean sol(long mid) {
        long cost = 0;
        for (int i = 0; i &lt; N; i++) {
            if (performance[i] &gt;= mid) return true;
            cost += Math.pow(mid-performance[i], 2);
            if (cost &gt; B) {
                return false;
            };
        }
        return true;
    }
}</code></pre>
<h3 id="결과-1">결과</h3>
<p>정답.
<img src="https://velog.velcdn.com/images/hyeok_b2ng/post/e168f57e-a573-4616-bbcc-62dc403069f3/image.png" alt=""></p>
<h2 id="리뷰">리뷰</h2>
<p>문제를 읽고, 그래프 알고리즘을 다 제외하고 dp랑 그리디를 생각하였다. 하지만 이 문제는 두 가지 모두 아니므로 수학적 사고력을 요하는 문제라고 생각해서 어떻게 시간을 줄일지만 생각하였다. but, 이진탐색으로 서칭을 줄이는 문제였다. 다음부터는 어떤 값을 찾아서 결과를 도출해야한다면 이진탐색을 떠올려야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[최소 신장 트리(MST)]]></title>
            <link>https://velog.io/@hyeok_b2ng/%EC%B5%9C%EC%86%8C-%EC%8B%A0%EC%9E%A5-%ED%8A%B8%EB%A6%ACMST</link>
            <guid>https://velog.io/@hyeok_b2ng/%EC%B5%9C%EC%86%8C-%EC%8B%A0%EC%9E%A5-%ED%8A%B8%EB%A6%ACMST</guid>
            <pubDate>Thu, 27 Jun 2024 14:56:46 GMT</pubDate>
            <description><![CDATA[<h2 id="최소-신장-트리란">최소 신장 트리란?</h2>
<p>그래프의 모든 정점을 사이클 없이 잇는 트리에서 간선의 <strong>가중치의 합이 최소</strong>가 되는 트리</p>
<ul>
<li>그래프에서 최소 비용 문제<ol>
<li>모든 정점을 연결하는 간선들의 <strong>가중치의 합이 최소</strong></li>
<li>두 정점 사이의 최소 비용의 경로 찾기</li>
</ol>
</li>
<li>신장 트리
  n개의 정점으로 이루어진 무향 그래프에서 n개의 정점과 n-1개의 간선으로 이루어진 트리</li>
<li><blockquote>
<p>최소 신장 트리 (Minimum Spanning Tree)는 가중치의 합이 <strong>최소</strong>인 신장 트리 !</p>
</blockquote>
</li>
</ul>
<p>그러면 어떻게 구할 수 있을까?</p>
<ul>
<li>간선 중심으로 최소 신장트리를 구하자</li>
<li>한 정점에서 하나의 간선은 무조건 존재하므로 정점에서 최소인 것을 연결 (?)</li>
</ul>
<p>바로 이 두 방법이 유명한 MST알고리즘인 KRUSKAL 알고리즘과 PRIM 알고리즘이다.</p>
<h2 id="kruskal-알고리즘-크루스칼-알고리즘">KRUSKAL 알고리즘 (크루스칼 알고리즘)</h2>
<p>간선 중심으로 최소 신장 트리를 구하는 알고리즘</p>
<ul>
<li>간선이 적다면 유리.<h3 id="방법">방법</h3>
</li>
</ul>
<ol>
<li>최초, 모든 간선을 가중치에 따라 오름차순 정렬</li>
<li>그 중 가장 가중치가 작은 간선부터 선택
2-1. 사이클이 존재 ? 해당 간선을 제외, 그 다음으로 낮은 간선 선택</li>
<li>n-1개가 될 때까지 반복</li>
</ol>
<p>※ 사이클 여부는 어떻게 확인 ? Union-Find를 사용하자</p>
<h3 id="boj1922_네트워크-연결">BOJ1922_네트워크 연결</h3>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class Main {
    static int N;
    static int[] p;
    static class Node implements Comparable&lt;Node&gt;{
        int a;
        int b;
        int cost;
        public Node(int a, int b, int cost) {
            this.a = a;
            this.b = b;
            this.cost = cost;
        }
        @Override
        public int compareTo(Node o) {
            return cost - o.cost;
        }
    }
    public static void main(String[] args) throws Exception{
        BufferedReader br = new BufferedReader((new InputStreamReader(System.in)));
        N = Integer.parseInt(br.readLine());
        int M = Integer.parseInt(br.readLine());

        p = new int[N+1];
        for (int i = 0; i &lt;= N; i++) p[i] = i;

        PriorityQueue&lt;Node&gt; pq = new PriorityQueue&lt;&gt;();
        for (int i = 0; i &lt; M; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());
            int cost = Integer.parseInt(st.nextToken());
            pq.offer(new Node(a, b, cost));
        }
        int edgeCnt = 0;
        int ans = 0;
        while (edgeCnt != N-1) {
            Node now = pq.poll();
            if (union(now.a, now.b)) {
                edgeCnt++;
                ans += now.cost;
            }
        }
        System.out.println(ans);
        br.close();
    }
    static boolean union(int x, int y) {
        int pX = find(x);
        int pY = find(y);

        if (pX == pY) return false;

        if (pX &lt; pY) p[pX] = pY;
        else p[pY] = pX;
        return true;
    }
    static int find(int x) {
        if (p[x] == x) return x;
        return p[x] = find(p[x]);
    }
}</code></pre>
<h2 id="prim-알고리즘">PRIM 알고리즘</h2>
<p>정점 중심으로 최소 신장 트리를 구하는 알고리즘</p>
<ul>
<li>정점이 적다면 유리.<h3 id="방법-1">방법</h3>
</li>
</ul>
<ol>
<li>임의의 정점 선택</li>
<li>선택한 정점과 인접하는 정점들 중의 최소 비용의 간선이 존재하는 정점을 선택</li>
<li>그 간선이 연결하는 정점 선택, 모든 정점이 선택될 때까지 2과정 반복</li>
</ol>
<h3 id="boj6497_전력난">BOJ6497_전력난</h3>
<pre><code class="language-java">package blog;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class MST_Prim {
    static class Edge {
        int to;
        int cost;
        public Edge(int to, int cost) {
            this.to = to;
            this.cost = cost;
        }
    }
    public static void main(String[] args) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        while (true) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int m = Integer.parseInt(st.nextToken());
            int n = Integer.parseInt(st.nextToken());
            if (m == 0 &amp;&amp; n == 0) break;
            ArrayList&lt;ArrayList&lt;Edge&gt;&gt; edgeList = new ArrayList&lt;&gt;();
            for (int i = 0; i &lt; m; i++) edgeList.add(new ArrayList&lt;&gt;());

            PriorityQueue&lt;Edge&gt; pq = new PriorityQueue&lt;&gt;((o1, o2)-&gt;(o1.cost-o2.cost));
            int total = 0;
            for (int i = 0; i &lt; n; i++) {
                st = new StringTokenizer(br.readLine());
                int a = Integer.parseInt(st.nextToken());
                int b = Integer.parseInt(st.nextToken());
                int cost = Integer.parseInt(st.nextToken());
                edgeList.get(a).add(new Edge(b, cost));
                edgeList.get(b).add(new Edge(a, cost));
                total += cost;
            }

            boolean[] isVisited = new boolean[m];
            int useCost = 0;
            pq.offer(new Edge(0, 0));
            while(!pq.isEmpty()) {
                Edge now = pq.poll();
                if (isVisited[now.to]) continue;
                isVisited[now.to] = true;
                useCost += now.cost;

                for (Edge edge : edgeList.get(now.to)) {
                    if (!isVisited[edge.to]) pq.offer(edge);
                }
            }
            sb.append(total-useCost).append(&quot;\n&quot;);
        }
        System.out.print(sb);
        br.close();
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[서로소 집합 (Union Find)]]></title>
            <link>https://velog.io/@hyeok_b2ng/%EC%84%9C%EB%A1%9C%EC%86%8C-%EC%A7%91%ED%95%A9-Union-Find</link>
            <guid>https://velog.io/@hyeok_b2ng/%EC%84%9C%EB%A1%9C%EC%86%8C-%EC%A7%91%ED%95%A9-Union-Find</guid>
            <pubDate>Thu, 27 Jun 2024 12:29:38 GMT</pubDate>
            <description><![CDATA[<h2 id="서로소-집합-disjoint-set">서로소 집합 (Disjoint-set)</h2>
<h3 id="서로소-집합이란">서로소 집합이란?</h3>
<p>공통 원소가 없는 두 집합을 의미한다. 즉, 서로 중복 포함된 원소가 없는 집합이고 교집합이 없다.</p>
<ul>
<li>집합에 속한 하나의 대표자를 통해 각 집합들을 구분<h3 id="서로소-집합-연산">서로소 집합 연산</h3>
</li>
<li>Make set(x)</li>
<li>Find set(x)</li>
<li>Union(x, y)<h2 id="서로소를-표현하는-방법">서로소를 표현하는 방법</h2>
</li>
<li>연결리스트</li>
<li>트리<h3 id="연결리스트">연결리스트</h3>
</li>
<li>같은 집합의 원소들은 하나의 연결리스트로 관리한다.</li>
<li>연결리스트의 맨 앞의 원소를 집합의 대표자로 선정한다.</li>
<li>각 원소는 집합의 대표원소를 가리키는 링크를 가진다.<h3 id="트리">트리</h3>
</li>
<li>같은 집합의 원소들은 하나의 트리로 표현한다.</li>
<li>자식 노드가 부모 노드를 가리키며 루트 노드가 대표자가 된다.<h2 id="구현-방법">구현 방법</h2>
앞서 말했던 3가지 연산을 차례로 구현해야한다.
1) 초기 - make set으로 모두 각각 독립된 집합으로 분리한다.<pre><code class="language-java">static int[] MakeSet(int size) {
  int[] parent = new int[size + 1];
  for (int i = 0; i &lt;= size; i++) {
      parent[i] = i;
  }
  return parent;
}</code></pre>
2) Find Set(x) : x를 포함하는 집합을 찾는 연산<pre><code class="language-java">static int find(int x) {
  if (x == parent[x]) return x;
  return find(p[x]);
}</code></pre>
3) Union(x, y) : x, y를 포함하는 두 집합을 합치는 연산<pre><code class="language-java">static void union(a, b) {
  int parentA = find(a);
  int parentB = find(b);
  if (parentA == parentB) return;
  parent[b] = parent[a];
}</code></pre>
<h3 id="문제점">문제점</h3>
계속 find 연산을 실행하는 x가 트리의 리프노드인 경우 -&gt; 많은 연산을 통해 계속 대표자를 찾게 됨.<h3 id="해결방안">해결방안</h3>
</li>
<li>Rank를 이용<ul>
<li>각 노드는 자신을 루트로 하는 subtree의 높이를 rank로 저장한다</li>
<li>union 시 rank가 낮은 집합을 rank가 높은 집합에 붙인다.</li>
</ul>
</li>
<li>Path compression<ul>
<li>Find 연산을 할 시 만나는 모든 노드들이 직접 root를 가리키도록 포인터를 업데이트 해준다.<h3 id="path-compression-적용">Path Compression 적용</h3>
Find Set<pre><code class="language-java">static int find(int x) {
if (x == parent[x]) return x;
return parent[x] = find(p[x]);
}</code></pre>
<h2 id="예시-문제-boj1976_여행가자">예시 문제 (BOJ1976_여행가자)</h2>
<a href="https://www.acmicpc.net/problem/1976">https://www.acmicpc.net/problem/1976</a><pre><code class="language-java">package blog;
</code></pre>
</li>
</ul>
</li>
</ul>
<p>import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;</p>
<p>public class DisjointSet {
    static int[] parent;
    public static void main(String[] args) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine());
        int M = Integer.parseInt(br.readLine());</p>
<pre><code>    // Make Set
    parent = new int[N+1];
    for (int i = 1; i &lt;= N; i++) parent[i] = i;

    for (int from = 1; from &lt;= N; from++) {
        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int to = 1; to &lt;= N; to++) {
            int link = Integer.parseInt(st.nextToken());
            if (link == 1) {
                union(from, to);
            }
        }
    }
    int target = 0;
    boolean possible = true;
    StringTokenizer st = new StringTokenizer(br.readLine());
    for (int i = 0; i &lt; M; i++) {
        int city = Integer.parseInt(st.nextToken());
        if (i == 0) target = find(city);
        else {
            int parentCity = find(city);
            if (target != parentCity) {
                possible = false;
                break;
            }
        }
    }
    System.out.println(possible ? &quot;YES&quot; : &quot;NO&quot;);
    br.close();
}
static int find(int x) {
    if (x == parent[x]) return x;
    return parent[x] = find(parent[x]);
}
static void union(int x, int y) {
    int parentX = find(x);
    int parentY = find(y);
    if (parentX == parentY) return;
    parent[parentY] = parent[parentX];
}</code></pre><p>}</p>
<p>```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[위상정렬 알고리즘 (BOJ_14567 선수과목)]]></title>
            <link>https://velog.io/@hyeok_b2ng/%EC%9C%84%EC%83%81%EC%A0%95%EB%A0%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-BOJ14567-%EC%84%A0%EC%88%98%EA%B3%BC%EB%AA%A9</link>
            <guid>https://velog.io/@hyeok_b2ng/%EC%9C%84%EC%83%81%EC%A0%95%EB%A0%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-BOJ14567-%EC%84%A0%EC%88%98%EA%B3%BC%EB%AA%A9</guid>
            <pubDate>Thu, 27 Jun 2024 11:22:34 GMT</pubDate>
            <description><![CDATA[<h2 id="문제상황">문제상황</h2>
<p>여러가지 수업을 듣는다고 가정하자.
각 수업을 수강하기위해서는 먼저 공부해야하는 선수과목들이 존재한다고 할 때, 즉 수강해야하는 과목의 순서가 있다고 할 때 수강할 수 있는 과목의 순서는?
이러한 선후관계가 존재할 때, 순서를 찾아야 한다면 위상 정렬을 사용할 수 있다.
또한, 모든 노드들이 선후관계가 정해져 있지 않을 수 있으므로 여러 가지가 존재할 수 있다.
주의 ) 위상정렬을 사용하기 위해서는 그래프가 순환하지 않아야한다.</p>
<h2 id="위상정렬이란">위상정렬이란?</h2>
<ul>
<li>유향 그래프의 정점들을 변의 반향을 거스르지 않도록 나열하는 것을 의미한다.</li>
<li>위상 정렬은 순서가 정해져 있는 작업들을 차례대로 수행해야 할 때, 그 순서를 결정해주는 알고리즘이다.</li>
<li>선후 관계가 정의된 그래프 구조 상에서 선후 관계에 따라 정렬하기 위해 위상정렬을 이용할 수 있다.</li>
<li>위상 정렬이 성립하기 위해서는 반드시 그래프의 순환이 존재하지 않아야 한다. 즉, 그래프가 비순환 유향 그래프(Directed Acyclic Graph)여야 한다.<h3 id="결과">결과</h3>
</li>
<li>위상 정렬이 가능하다? -&gt; 사이클 발생 여부 확인 가능</li>
<li>가능하다면 정렬된 결과<h2 id="구현방법">구현방법</h2>
</li>
<li>BFS (큐)</li>
<li>DFS (재귀)<h3 id="bfs">BFS</h3>
</li>
</ul>
<ol>
<li>그래프의 각 노드들의 진입 차수 테이블 생성 및 진입 차수 계산</li>
<li>진입 차수가 0인 노드(시작점)를 큐에 모두 넣는다.</li>
<li>큐에서 진입 차수가 0인 노드를 꺼내어 자신과 인접한 노드의 간선을 제거한다.
 -&gt; 인접한 노드의 진입 차수를 1 감소시킨다.</li>
<li>간선 제거 후 진입 차수가 0이 된 노드를 큐에 넣는다.</li>
<li>큐가 빌 때까지 3-4을 반복한다.
 -&gt; 모든 노드 처리 시 위상정렬 ok.
 -&gt; 모든 노드 처리되지 않는다면 사이클 발생<h3 id="위상-정렬을-bfs로-구현-with-java-boj14567-선수과목">위상 정렬을 BFS로 구현 with Java (BOJ14567 선수과목)</h3>
<a href="https://www.acmicpc.net/problem/14567">https://www.acmicpc.net/problem/14567</a><pre><code class="language-java">package blog;
</code></pre>
</li>
</ol>
<p>import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.*;</p>
<p>// BOJ 14567 선수과목
public class TopologicalSort {</p>
<pre><code>public static void main(String[] args) throws Exception{
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    StringTokenizer st = new StringTokenizer(br.readLine());
    int N = Integer.parseInt(st.nextToken());
    int M = Integer.parseInt(st.nextToken());

    // 진입차수
    int[] edgeCount = new int[N+1];

    // 그래프
    ArrayList&lt;ArrayList&lt;Integer&gt;&gt; edgeList = new ArrayList&lt;&gt;();
    for (int i = 0; i &lt;= N; i++) edgeList.add(new ArrayList&lt;&gt;());

    for (int i = 0; i &lt; M; i++) {
        st = new StringTokenizer(br.readLine());
        int A = Integer.parseInt(st.nextToken());
        int B = Integer.parseInt(st.nextToken());
        edgeList.get(A).add(B);
        edgeCount[B]++;
    }
    // 선수과목번호
    Queue&lt;int[]&gt; q = new ArrayDeque&lt;&gt;();
    int[] ans = new int[N+1];
    for (int i = 1; i &lt;= N; i++) {
        if (edgeCount[i] == 0) q.offer(new int[] {i, 1});
    }
    while (!q.isEmpty()) {
        int[] from = q.poll();
        int A = from[0];
        ans[A] = from[1];
        for (int i = 0; i &lt; edgeList.get(A).size(); i++) {
            int to = edgeList.get(A).get(i);
            edgeCount[to]--;
            if (edgeCount[to] == 0) q.offer(new int[] {to, ans[A] + 1});
        }
    }
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
    for (int i = 1; i &lt;= N; i++) bw.write(String.valueOf(ans[i]) + &quot; &quot;);
    bw.flush();
    br.close();
}</code></pre><p>}</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[캡슐화와 다형성]]></title>
            <link>https://velog.io/@hyeok_b2ng/Encapsulation-%EC%BA%A1%EC%8A%90%ED%99%94</link>
            <guid>https://velog.io/@hyeok_b2ng/Encapsulation-%EC%BA%A1%EC%8A%90%ED%99%94</guid>
            <pubDate>Sun, 21 Jan 2024 05:18:05 GMT</pubDate>
            <description><![CDATA[<h1 id="encapsulation-캡슐화">Encapsulation (캡슐화)</h1>
<h2 id="데이터-은닉과-보호">데이터 은닉과 보호</h2>
<blockquote>
<p>데이터 은닉</p>
</blockquote>
<ul>
<li>public으로 선언된 것들을 private접근으로 막자</li>
<li>공개되는 메서드를 통한 접근 통로 마련
  -setter / getter</li>
</ul>
<blockquote>
<p>데이터 보호</p>
</blockquote>
<ul>
<li>setter / getter메소드 내부에서 데이터 보호에 대한 로직 작성</li>
</ul>
<p>디자인패턴</p>
<ul>
<li>어떠한 목적을 달성하기 위해 사람들의 경험에서 나온 패턴</li>
</ul>
<blockquote>
<p>Singleton 디자인패턴</p>
</blockquote>
<ul>
<li>객체의 생성을 제한해야하는 경우<ol>
<li>여러개의 객체가 필요 없는 경우<ul>
<li>stateless객체 : 객체를 구별할 필요가 없는 경우, 기능만 있는경우</li>
</ul>
</li>
<li>객체를 계속 생성/삭제하는데 많은 비용이 들어서 재사용이 유리한 경우</li>
</ol>
</li>
<li>만드는 방법<ol>
<li>외부에서 생성자 접근 금지 - 생성자의 접근 제한자를 private</li>
<li>내부에서는 private에 접근 가능하므로 직접 객체 생성 - 멤버변수 private</li>
<li>외부에서 private member에 접근 가능한 getter생성</li>
<li>객체 생성없이 접근가능하도록 static추가</li>
</ol>
</li>
</ul>
<h1 id="polymorphism다형성">Polymorphism(다형성)</h1>
<h2 id="다형성">다형성</h2>
<ul>
<li>하나의 객체가 많은 형(타입)을 가질 수 있는 성질</li>
<li>상속관계에서 조상 클래스의 타입으로 자식 클래스 객체를 레퍼런스 가능</li>
<li>Object는 모든 클래스의 조상 -&gt; Object의 배열은 어떤 타입의 객체라도 다 저장할 수 있음</li>
<li>기본형은 Object를 상속 받지 않았음
  -auto boxing으로 Wrapper클래스를 통해 저장됨</li>
<li>API에서 파라미터로 Object를 받는다는 것을 모든 타입을 다 파라미터로 받을 수 있음 !</li>
</ul>
<h2 id="객체의-형-변환">객체의 형 변환</h2>
<p>메모리에 있더라도 참조하는 변수의 타입에 따라 접근할 수 있는 내용이 제한.
자식을 부모타입으로 받으면 자식의 기능이 메모리에 있지만 사용할 수 없다.</p>
<ul>
<li>사용할려면 명시적 캐스팅을 통해 상위타입을 하위타입으로 형 변환 후 사용 !
참고) 하위 타입을 상위타입으로 형변환할 때는 묵시적 캐스팅이 일어남</li>
</ul>
<blockquote>
<p>상위 타입을 하위타입으로 형 변환할 시</p>
</blockquote>
<ul>
<li>무작정 자손으로 바꿀 수는 없다.
  -실행 시 오류 발생. 컴파일시 확인 불가</li>
<li>instanceof연산자를 사용하여 변경가능한 지 확인</li>
</ul>
<h3 id="참조-변수의-레벨에-따른-객체의-멤버-연결"><strong>참조 변수의 레벨에 따른 객체의 멤버 연결</strong></h3>
<pre><code class="language-java">class SuperClass {
    String x = &quot;super&quot;;
    public void method() {
        System.out.println(&quot;super class method&quot;);
    }
}

class SubClass extends SuperClass {
    String x = &quot;sub&quot;;
    @Override
    public void method() {
        System.out.println(&quot;sub class method&quot;);
    }
}

public static void main(String[] args) {
    SubClass subClass = new SubClass();
    System.out.println(subClass.x);
    subClass.method();

    SuperClass superClass = subClass;
    System.out.println(superClass.x);
    superClass.method();
}
</code></pre>
<p>위 코드의 결과는 ?
sub
sub class method
super
sub class method</p>
<blockquote>
<p>정적바인딩</p>
</blockquote>
<ul>
<li>컴파일 단계에서 참조 변수의 타입에 따라 연결이 달라짐</li>
<li>상속 관계에서 객체의 멤버변수(static/instance)가 중복될 때 또는 static method</li>
</ul>
<blockquote>
<p>동적바인딩</p>
</blockquote>
<ul>
<li>다형성을 이용해서 메서드 호출이 발생할 때 runtime에 메모리의 실제 객체의 타입으로 결정</li>
<li>상속 관계에서 객체의 instance method가 재정의(오버라이드)되었을 때 마지막에 재정의 된 자식 클래스의 메서드가 호출됨</li>
</ul>
<p>println()은 toString()이 출력됨. 따라서 자식 클래스에서 재정의해서 사용.</p>
<p>상위 타입으로 올라갈 수록 활용도가 높아지지만 복잡성이 증가 !
따라서 많은 경우 비즈니스 로직 상 최상위 객체를 사용하는 것이 좋다.</p>
<h2 id="몇가지-메서드재정의">몇가지 메서드재정의</h2>
<h3 id="tostring">toString()</h3>
<ul>
<li>객체를 문자열로 변경하는 매서드<pre><code class="language-java">public String toString() {
  return getClass().getName() + &quot;@&quot; + Integer.toHexString(hashCode());
}</code></pre>
</li>
<li>주소값이 반환되므로 클래스에서 재정의해서 사용.<h3 id="equals">equals()</h3>
</li>
<li>두 객체가 같은지를 비교하는 메서드</li>
<li>원래는 주소값 비교 (==)</li>
<li>따라서 equals를 내용비교로 재정의.<h3 id="hashcode">hashCode()</h3>
</li>
<li>객체의 해시 코드 : 시스템에서 객체를 구별하기 위한 값</li>
<li>equals 매서드를 재정의할 때는 반드시 hashCode도 함께 재정의</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Inheritance(상속)]]></title>
            <link>https://velog.io/@hyeok_b2ng/Inheritance%EC%83%81%EC%86%8D</link>
            <guid>https://velog.io/@hyeok_b2ng/Inheritance%EC%83%81%EC%86%8D</guid>
            <pubDate>Sun, 21 Jan 2024 04:44:37 GMT</pubDate>
            <description><![CDATA[<h2 id="상속">상속</h2>
<ul>
<li>기존(상위)클래스의 자산(멤버)을 자식(하위)클래스에서 재사용하기 위한 것</li>
<li>상위 클래스의 멤버를 물려받기 떄문에 코드 절감</li>
<li>extends 키워드 사용 (확장할 수 있다는 것이 중요)</li>
</ul>
<p>모든 클래스의 조상 클래스는 <strong>Object클래스</strong>임</p>
<ul>
<li>별도의 extends 선언이 없는 클래스는 extends Object가 생략</li>
</ul>
<p>상속관계는 is a 관게라고 한다.</p>
<h3 id="단일-상속">단일 상속</h3>
<ul>
<li>다중 상속의 경우 관계가 복잡해진다.<ul>
<li>상위 클래스에서 메서드 이름이 같다면 무엇을 쓸지 정할 수 없음</li>
</ul>
</li>
<li>따라서 자바에서는 단일 상속만 지원
but, interface와 포함 관계 사용</li>
</ul>
<h3 id="포함-관계">포함 관계</h3>
<ul>
<li>has a 관계</li>
<li>2개 이상의 클래스에서 특성을 가져올 때 하나는 상속, 나머지는 멤버변수로 처리</li>
</ul>
<h2 id="메서드-재정의">메서드 재정의</h2>
<h3 id="메서드-오버라이딩">메서드 오버라이딩</h3>
<ul>
<li>조상 클래스에 정의된 메서드를 자식 클래스에서 적합하게 수정하는 것</li>
<li>물려받은 메서드의 기능을 자식 클래스에서 수정하는 것</li>
</ul>
<blockquote>
<p>조건</p>
</blockquote>
<ul>
<li>메서드 이름이 같아야한다.</li>
<li>매개변수의 개수, 타입, 순서가 같아야한다.</li>
<li>리턴타입이 같아야한다.</li>
<li>접근 제한자는 부모보다 범위가 넓거나 같아야한다.</li>
<li>조상보다 더 큰 예외를 던질 수 없다.</li>
</ul>
<p>super 키워드</p>
<ul>
<li>super.을 이용해 조상의 메서드 호출로 조상의 코드 재사용</li>
<li>super()는 조상 클래스의 생성자 호출</li>
<li>super()는 자식 클래스 생성자의 맨 첫 줄에서만 호출 가능</li>
<li>명시적으로 this() 또는 super()를 호출하지 않는 경우 컴파일러가 super()삽입</li>
</ul>
<h3 id="annotation">Annotation</h3>
<ul>
<li>주석</li>
<li>컴파일러, JVM, 프레임워크등이 보는 주석</li>
<li>소스코드에 메타 데이터를 삽입하는 형태</li>
<li>ex) @Deprecated, @Override, @SuppressWarnings</li>
</ul>
<h2 id="package--import">Package &amp; Import</h2>
<h3 id="package">Package</h3>
<ul>
<li>물리적으로 패키지는 클래스 파일을 담고 있는 디렉터리</li>
<li>상단에 선언. 하나만</li>
<li>생략시 기본패키지 but, 권장하지 않음</li>
</ul>
<p>일반적인 package naming룰</p>
<ul>
<li>소속.프로젝트.용도.~</li>
</ul>
<h3 id="import">import</h3>
<ul>
<li>패키지와 달리 여러번 선언 가능</li>
<li><ul>
<li>모든 것을 가져옴</li>
</ul>
</li>
<li>ctrl + space</li>
<li>ctrl + shift + o</li>
</ul>
<h2 id="제한자modifier">제한자(modifier)</h2>
<ul>
<li>하나의 대상에 여러 제한자를 조합 가능하나 접근 제한자는 하나만 사용 가능</li>
<li>순서 무관
  -일반적으로 접근 제한자를 맨 앞으로<h3 id="종류">종류</h3>
</li>
<li>접근 제한자 : public, protected, default, private</li>
<li>그 외 제한자 : static, final, abstract, ...<h3 id="final">final</h3>
</li>
<li>마지막, 더 이상 바뀔 수 없음</li>
<li>실수하지않게 도와주는 역할</li>
<li>final class : 더 이상 확장 할 수 없음 : 상속금지 -&gt; 오버라이드 방지
  -ex) Math, String, ...</li>
<li>final method : 더 이상 재 정의 할 수 없음 ; 오버라이드 금지</li>
<li>final variable : 더 이상 값을 바꿀 수 없음 (상수)</li>
<li>Blank final : 값이 할당되지 않은 멤버 변수<ul>
<li>생성자에서 1회 초기화 가능</li>
</ul>
</li>
<li>static final</li>
</ul>
<p>public : 클래스, 생성자, 멤버
protected : 생성자, 멤버
package(default) : 클래스, 생성자, 멤버
private : 생성자, 멤버</p>
<p>public : 같은 클래스, 같은 패키지, 다른 패키지의 자손클래스, 전체
protected : 같은 클래스, 같은 패키지, 다른 패키지의 자손클래스
package(default) : 같은 클래스, 같은 패키지
private : 같은 클래스</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Abstraction(추상화)]]></title>
            <link>https://velog.io/@hyeok_b2ng/Abstraction%EC%B6%94%EC%83%81%ED%99%94</link>
            <guid>https://velog.io/@hyeok_b2ng/Abstraction%EC%B6%94%EC%83%81%ED%99%94</guid>
            <pubDate>Sun, 21 Jan 2024 01:28:18 GMT</pubDate>
            <description><![CDATA[<h2 id="객체지향-프로그래밍">객체지향 프로그래밍</h2>
<blockquote>
<p>객체</p>
</blockquote>
<ol>
<li>객체란 ?
주체가 아닌 것, 주체가 활용하는 것</li>
<li>지향 ?
객체를 많이 만드는 것을 추천</li>
<li>객체지향 프로그래밍의 장점
추가 수정 삭제가 용이
재사용성이 높다</li>
</ol>
<blockquote>
<p>Class와 Object</p>
</blockquote>
<ul>
<li>현실의 객체가 갖는 속성과 기능은 추상화되어 클래스에 정의</li>
<li>클래스는 구체화되어 프로그램의 객체가 됨</li>
<li>현실의 객체는 우리가 만지고 느낄 수 있는 것</li>
<li>ex) 붕어빵틀(class) 붕어빵(object)</li>
<li>추상화한 결과가 class</li>
<li>이를 구체화하는 것이 바로 object(객체)</li>
</ul>
<p>클래스</p>
<ul>
<li>객체를 정의해 놓은 것</li>
<li>틀</li>
<li>데이터 타입</li>
</ul>
<p>객체</p>
<ul>
<li>클래스를 데이터 타입으로 메모리에 생성되어 실제로 동작하는 것</li>
</ul>
<blockquote>
<p>객체 생성과 메모리
JVM의 메모리 구조</p>
</blockquote>
<ul>
<li>meta-space
클래스 정보</li>
<li>stack
메서드들의 실행 공간</li>
<li>heap
객체를 저장하기 위한 영역
가비지 컬렉션으로 정리
상수풀</li>
</ul>
<h2 id="변수">변수</h2>
<p>선언 위치에 따른 분류
1.멤버변수</p>
<ul>
<li>클래스 영역(static) - 클래스 멤버 변수</li>
<li>클래스 영역 - 인스턴스 멤버 변수</li>
</ul>
<ol start="2">
<li>지역변수</li>
</ol>
<ul>
<li>함수 내부 - 지역변수</li>
<li>함수 선언부 - 파라미터 변수</li>
</ul>
<blockquote>
<p>인스턴스 멤버 변수의 특징
생성 : 객체가 만들어질 때
초기화 : 타입별로 기본값
접근 : 객체 생성 후 객체 이름으로 접근
소멸시점 : Garbage Collector에 의해 객체가 없어질 때 (명시적 x)</p>
</blockquote>
<blockquote>
<p>클래스 멤버 변수의 특징
- static 키워드가 핵심
- 변할 수 있지만 거의 변하지 않는 것
- 모든 객체가 공유
생성 : 클래스가 로딩될 때
초기화 : 타입 별로 기본값
접근 : 클래스 이름으로 접근
소멸시점 : 클래스가 언로드될 때</p>
</blockquote>
<blockquote>
<p>지역 변수와 파라미터 변수
- 클래스 영역의 {}이외의 모든 중괄호안
생성 : 선언된 라인이 실행
초기화 : 명시적 초기화
접근 : 외부에서 접근 불가
소멸 시점 : {}을 벗어날 때</p>
</blockquote>
<h2 id="메서드">메서드</h2>
<blockquote>
<p>메서드 정의, 필요성</p>
</blockquote>
<ol>
<li>메서드 정의</li>
</ol>
<ul>
<li>현실의 객체가 하는 동작을 프로그래밍화</li>
<li>어떤 작업을 수행하는 명령문의 집합</li>
</ul>
<ol start="2">
<li>필요성</li>
</ol>
<ul>
<li>중복 방지</li>
<li>코드의 길이를 줄이고, 유지보수</li>
</ul>
<p>선언부</p>
<ul>
<li>메서드를 호출하는 곳, 반환되는 값의 타입으로 아무것도 리턴하지 않을 경우 void</li>
<li>결과를 받을 때 묵시적 형 변환</li>
<li>리턴타입은 하나</li>
<li>메서드 호출 시 넘겨줘야 하는 변수들은 파라미터로 선언</li>
<li>메서드 선언 시 동일 타입의 인자가 몇 개 들어올 지 예상할 수 없을 경우<ul>
<li>배열 타입 선언이 가능하지만 호출 전 생성 초기화의 번거로움</li>
<li><strong>...</strong>을 이용해 파라미터를 선언하면 호출 시 넘겨준 값의 개수에 따라 자동으로 배열 생성 및 초기화</li>
</ul>
</li>
</ul>
<p>구현부</p>
<ul>
<li>중괄호 내에서 처리해야 하는 내용</li>
<li>마지막에는 return</li>
</ul>
<p>호출은 반드시 선언부에 맞춰서 호출</p>
<ul>
<li>메서드의 이름과 파라미터</li>
</ul>
<p>메서드도 변수와 마찬가지로 static or non static상태를 구분</p>
<p>static member -&gt; 언제나 메모리에 있음
instance member -&gt; 객체 생성 전에는 메모리에 없음</p>
<p>메서드는 스택의 구조로 실행</p>
<blockquote>
</blockquote>
<p>메서드 오버로딩</p>
<ul>
<li>동일한 기능을 수행하는 메서드 추가 작성</li>
<li>예시로 println()
오버로딩의 장점</li>
<li>기억해야할 매서드가 감소하고 중복 코드에 대한 효율적 관리 가능
오버로딩 방법</li>
<li>메서드 이름은 동일</li>
<li>파라미터의 개수 또는 순서, 타입은 달라야 할 것</li>
<li>리턴 타입은 의미 없음</li>
</ul>
<h2 id="생성자">생성자</h2>
<blockquote>
<p>생성자</p>
</blockquote>
<ul>
<li>객체를 생성할 때 호출하는 메서드 비슷한 것</li>
<li>일반 멤버 변수위 초기화 및 객체 생성 시 실행돼야 하는 작업 정리</li>
</ul>
<blockquote>
<p>생성자의 종류</p>
</blockquote>
<ol>
<li>기본생성자</li>
</ol>
<ul>
<li>파라미터가 없고, 구현부가 비어있는 형태</li>
<li>생성자 코드가 없으면 컴파일러가 기본 생성자 제공</li>
</ul>
<ol start="2">
<li>파라미터가 있는 생성자</li>
</ol>
<ul>
<li>생성자 호출 시 값을 넘겨줘서 멤버 변수 초기화</li>
<li><strong>파라미터가 있는 생성자를 만들면 기본 생성자는 추가되지 않는다 !</strong></li>
<li>따라서 기본 생성자를 추가해줘야함.</li>
</ul>
<p>this.</p>
<ul>
<li>참조 변수로써 객체 자신을 가리킴, 자신의 멤버의 접근 가능</li>
<li>용도 : 로컬 변수와 멤버 변수의 이름이 동일할 경우 멤버 변수임을 명시적으로 나타냄</li>
<li>객체에 대한 참조이므로 static영역에서는 this 사용불가</li>
<li>메서드와 마찬가지로 생성자도 오버로딩 가능</li>
<li>반드시 첫 줄에서만 호출이 가능 (생성자내부에서 생성자를 호출시)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java 기본 문법]]></title>
            <link>https://velog.io/@hyeok_b2ng/Java-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95</link>
            <guid>https://velog.io/@hyeok_b2ng/Java-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95</guid>
            <pubDate>Sat, 20 Jan 2024 10:18:48 GMT</pubDate>
            <description><![CDATA[<h2 id="1-자바-기본">1. 자바 기본</h2>
<blockquote>
<p><strong>Variable</strong>이란?</p>
</blockquote>
<ul>
<li>자료를 저장하기 위한 메모리 공간으로 <strong>타입에 따라 크기가 달라짐</strong></li>
<li>메모리 공간에 값을 할당 후 사용</li>
</ul>
<p>Type이란?</p>
<ul>
<li>데이터 타입에 따른 데이터의 종류</li>
</ul>
<table>
<thead>
<tr>
<th align="left">기본형</th>
<th align="left">참조형</th>
</tr>
</thead>
<tbody><tr>
<td align="left">미리 정해진 크기의 데이터 표현</td>
<td align="left">크기가 미리 정해질 수 없는 데이터의 표현</td>
</tr>
<tr>
<td align="left">변수 자체에 값 저장</td>
<td align="left">변수에는 실제 값을 참조할 수 있는 주소만 저장</td>
</tr>
</tbody></table>
<p>기본형의 갯수(8개)
논리형(Boolean)
정수형(byte, short, <strong>int</strong>, long(끝에L))
실수형(float(끝에 F), <strong>double</strong>)
문자형(char)
Q. byte는 8비트인데 2^7까지만 처리하는 이유? 
A. 맨 앞 비트는 부호비트 !</p>
<p>다음 코드의 실행 결과?</p>
<ol>
<li><pre><code class="language-java">public static void main(String[] args) {
 int i1 = Integer.MAX_VALUE;
 int i2 = i1 + 1;
 System.out.println(i2);
}</code></pre>
</li>
</ol>
<p>-&gt; overflow발생 ! (정수 계산 시 주의)
따라서, -2147483648가 출력</p>
<p>2.</p>
<pre><code class="language-java">public static void main(String[] args) {
    float f1 = 2.0f
    float f2 = 1.1f
    float f3 = f1 - f2;
    System.out.println(f3);

    double d1 = 2.0;
    double d2 = 1.1;
    double d3 = d1 - d2;
    System.out.println(d3)

        // 정확한 값 계산하기
    System.out.println(((int) (d1 * 100) - (int) (d2 * 100)) / 100.0);
    BigDecimal b1 = new BigDecimal(&quot;2.0&quot;);
    BigDecimal b2 = new BigDecimal(&quot;1.1&quot;);
    System.out.println(&quot;BigDecimal을 이용한 빼기 : &quot; + b1.subtract(b2));
}</code></pre>
<p>-&gt; 실수의 연산은 정확하지 않다. - 유효 자리수를 이용한 반올림 처리</p>
<h3 id="형변환">형변환</h3>
<p>형변환이란?</p>
<ul>
<li>변수의 형을 다른 형으로 변환하는 것</li>
<li>primitice는 primitive끼리, reference는 reference끼리<ul>
<li>기본형과 참조형의 형 변환을 위해서는 <strong>Wrapper클래스</strong> 사용 !</li>
<li>총 8개</li>
</ul>
</li>
</ul>
<p>형 변환 방법</p>
<ul>
<li>묵시적 형 변환 : 형 변환 연산자(괄호)사용<ul>
<li>값 손실 주의</li>
</ul>
</li>
<li>명시적 형 변환</li>
</ul>
<p>타입의 표현 범위가 커지는 방향으로 할당할 경우는 묵시적 형 변환 발생
ex) long(64비트) -&gt; float(32비트)
추가로 short(16비트) &lt;-&gt; char(16비트)는 불가
why? short는 부호비트를 사용하지만 char는 모든 비트를 사용하므로</p>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) {
        int i1 = Integer.MAX_VALUE;
        int i2 = i1 + 1;        // overflow
        System.out.println(i2);    // -2147483648

        long l1 = i1 + 1;        // overflow
        System.out.println(l1);    // -2147483648

        long l2 = (long) (i1 + 1);    // overflow 먼저 계산하므로
        System.out.println(l2);        // -2147483648

        long l3 = (long) i1 + 1;    
        System.out.println(l3);        // 2147483648

        int i3 = 1000000 * 1000000 / 100000;    // 깨지고 나눔
        int i4 = 1000000 / 100000 * 100000;    
        System.out.println(i3 + &quot; : &quot; + i4);    // -7273 : 1000000
    }
}</code></pre>
<pre><code class="language-java">public class Main {

    public static void main(String[] args) {
        byte b1 = 10;
        byte b2 = 20;
        byte b3 = b1 + b2;    // 에러발생 int이하이므로

        int i1 = 10;
        long l1 = 20;        // 자동 형변환
        int i2 = i1 + l1;    // 큰 타입 long으로

        float f1 = 10.0;        // 기본 double이므로 F로 !
        float f2 = f1 + 20.0;    // 큰 타입 double로
    }
}</code></pre>
<ul>
<li><p>자바에서는 연산 전에 피 연산자의 타입을 일치시킨다.</p>
</li>
<li><p>피연산자의 크기가 int(4바이트)미만이면 int로 변경한 후 연산 진행</p>
</li>
<li><p>두 개의 피 연산자 중 큰 타입으로 형 변환 후 연산 진행</p>
<pre><code class="language-java">public class Main {
  public static void main(String[] args) {

      int a = 10;
      int b = 20;
      System.out.println((a &gt; b) &amp; (b &gt; 0));    // false

      System.out.println((a += 10) &gt; 15 | (b -= 10) &gt; 15);    // true
      System.out.println(&quot;a = &quot; + a + &quot;, b = &quot; + b);    // 20, 10

      a = 10;
      b = 20;
      System.out.println((a += 10) &gt; 15 || (b -= 10) &gt; 15);    // true
      System.out.println(&quot;a = &quot; + a + &quot;, b = &quot; + b);    // 20, 20
  }
}</code></pre>
<p>why? ||연산은 앞이 true이면 뒤의 연산을 실행하지않음. but |연산은 두개의 연산 모두 실행.</p>
</li>
</ul>
<h3 id="조건문">조건문</h3>
<blockquote>
</blockquote>
<ol>
<li>if문에 들어갈 수 있는 type?
boolean, Boolean, boolean getResult()</li>
<li>switch문에 들어갈 수 있는 type?
byte, short, char, int, String, int getNumber()
- long타입은 안됨. 또한 정수형의 모든 타입 x</li>
</ol>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) {
        int num = 3;

        switch (num) {
        case 1:
            System.out.println(num);
        case 2:
            System.out.println(num);
        case 3:
            System.out.println(num);    // 실행
        case 4:
            System.out.println(num);    // 실행
        case 5:
            break;                        // 중단
        case 6:
            break;
        default:
            System.out.println(num);
        }

        int month = 3;
        int day = -1;
        switch (month) {
        case 2:
            day = 29;
            break;
        case 4:
        case 6:
        case 9:
        case 11:
            day = 30;
            break;
        default:
            day = 31;
            break;
        }
        System.out.printf(&quot;%d월은 %d까지 있다.%n&quot;, month, day);
    }
}
</code></pre>
<h3 id="반복문for-while">반복문(for, while)</h3>
<blockquote>
</blockquote>
<p>반복문의 4가지 구성요소</p>
<ul>
<li>변수 초기화</li>
<li>반복 조건</li>
<li>증감식</li>
<li>실행문</li>
</ul>
<pre><code class="language-java">public static void byFor() {
        int sum = 0;
        int cnt = 100;
        double avg = 0;
        Random rand = new Random();
        for (int i = 0; i &lt; cnt; i++) {
            sum += rand.nextInt(6) + 1;
        }
    }</code></pre>
<pre><code class="language-java">public static void byWhile() {
        int sum = 0;
        int cnt = 100;
        double avg = 0;
        Random rand = new Random();

        int i = 0;
        while (i &lt; cnt) {
            sum += rand.nextInt(6) + 1;
            i++;
        }
    }</code></pre>
<h2 id="2-배열-다차원-배열">2. 배열, 다차원 배열</h2>
<h3 id="배열">배열</h3>
<p>배열이란 ? 동일한 타입의 데이터 0개 이상을 하나의 연속된 메모리 공간에서 관리하는 것</p>
<h4 id="array만들기1">Array만들기#1</h4>
<blockquote>
<p>타입[] 변수명; or 타입 변수명[]
ex) int a; int[] arr
    - a의 타입 ? int
    - arr의 타입 ? int[] (참조형 타입)
    - arr이 저장하는 데이터의 타입 ? int</p>
</blockquote>
<blockquote>
<p>배열의 생성과 초기화</p>
</blockquote>
<ul>
<li>new 키워드와 함께 저장하려는 데이터의 타입 및 길이 지정</li>
<li>배열의 생성과 동시에 저장 대상 자료형에 대한 기본값으로 default초기화 진행</li>
<li>메모리의 연속된 공간 차지 -&gt; 크기 변경 불가 !!</li>
<li><table>
<thead>
<tr>
<th>자료형</th>
<th>기본값</th>
</tr>
</thead>
<tbody><tr>
<td>boolean</td>
<td>false</td>
</tr>
<tr>
<td>char</td>
<td>공백문자</td>
</tr>
<tr>
<td>byte, short, int</td>
<td>0</td>
</tr>
<tr>
<td>long</td>
<td>0L</td>
</tr>
<tr>
<td>flaot</td>
<td>0.0f</td>
</tr>
<tr>
<td>double</td>
<td>0.0</td>
</tr>
<tr>
<td>참조형 변수</td>
<td>null</td>
</tr>
</tbody></table>
</li>
</ul>
<p>출력을 편하게 하는 법
Arrays.toString()</p>
<pre><code class="language-java">String org = &quot;Hello&quot;;
char[] chars = new char[org.length()];
for (int i = 0; i &lt; chars.length; i++) {
    chars[i] = org.charAt(i);
}
for (int i = 0; i &lt; chars.length; i++) {
    System.out.print(chars[i]);
}

// -&gt; API
chars = org.toCharArray();
for (int i = 0; i &lt; chars.length; i++) {
    System.out.print(chars[i]);
}</code></pre>
<h4 id="array만들기2">Array만들기#2</h4>
<ul>
<li>생성과 동시에 할당한 값으로 초기화
  - int[] a = new int[] {1,2,3,4,5};
  - int[] b = {1,2,3,4,5};</li>
<li>선언과 생성을 따로 처리할 경우 초기화 주의
  - int[] c; c = {1,2,3,4,5}; // 컴파일 오류
  - 꼭 new를 사용 즉, int[] c; c = new int[]{1,2,3,4,5};로 사용</li>
</ul>
<h4 id="for-each">for-each</h4>
<ul>
<li>가독성이 개선된 반복문으로, 배열 및 Collections에서 사용<pre><code class="language-java">for (int i : arr) {
  //~~~
}</code></pre>
</li>
</ul>
<p><strong>Array는 변경불가 !</strong></p>
<ul>
<li><p>배열은 최초 메모리 할당이후, 변경할 수 없음</p>
</li>
<li><p>배열이 바뀌는 것이 아니라 새로운 배열을 선언하고 참조하는 것.</p>
<pre><code class="language-java">public static void main(String[] args) {
      int[] scores = { 90, 80, 100 };

      // TODO: 95점을 추가로 관리하기 부적절한 코드는?
      // scores[3] = 95; // #1

      // scores = new int[] {90, 80, 100, 95}; // #2

      // scores = {90, 80,100, 95 }; // #3

      // scores = Arrays.copyOf(scores, 5); // #4
      // scores[3]=95;

      // int[] scores2 = new int[4]; // #5
      // System.arraycopy(scores, 0, scores2, 0, scores.length);
      // scores2[3] = 95;

  }</code></pre>
</li>
<li><blockquote>
</blockquote>
<p>1: 넘치는 idex
3: new int[] 선언</p>
</li>
</ul>
<h3 id="2차원-배열">2차원 배열</h3>
<ol>
<li>2차원 배열 만들기#1</li>
</ol>
<ul>
<li>int[][] intArray;</li>
<li>int intArray[][];</li>
<li>int[] intArray[];
  2차원 배열은 1차원 배열을 저장하는 배열이다.</li>
</ul>
<ol start="2">
<li>2차원 배열 만들기#2</li>
</ol>
<ul>
<li>선언과 생성, 할당을 동시에</li>
<li>int[][] intArray = {{0, 1, 2}, {0, 1, 2}, {0, 1, 2}};</li>
</ul>
<ol start="3">
<li>2차원 배열 만들기#3</li>
</ol>
<ul>
<li>4x? 배열 만들기</li>
<li>int[][] intArray = new int[4][]</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>