<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Dev Uni</title>
        <link>https://velog.io/</link>
        <description>백엔드 데브코스 TIL (Today I Learned)</description>
        <lastBuildDate>Thu, 08 Jun 2023 14:55:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Dev Uni</title>
            <url>https://velog.velcdn.com/images/jae_yoon/profile/237bac64-c22c-403f-ac96-9c168ebabf55/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Dev Uni. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jae_yoon" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[백엔드 데브코스 TIL 4기 - 06.08(목)]]></title>
            <link>https://velog.io/@jae_yoon/%EB%B0%B1%EC%97%94%EB%93%9C-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-TIL-4%EA%B8%B0-06.08%EB%AA%A9</link>
            <guid>https://velog.io/@jae_yoon/%EB%B0%B1%EC%97%94%EB%93%9C-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-TIL-4%EA%B8%B0-06.08%EB%AA%A9</guid>
            <pubDate>Thu, 08 Jun 2023 14:55:25 GMT</pubDate>
            <description><![CDATA[<h2 id="💻-2주차-day-04">💻 2주차 DAY 04</h2>
<p>2주차 DAY 04.. 본격적인 계산기 과제를 시작했다. 계속 설계에만 몰두하다보니 정작 코드는 많이 짜지 못하였고, 금일 RBF나 잡혀있어서 생각보다 이래저래 시간을 많이 썼다.</p>
<br>

<h3 id="공유받은-꿀팁">공유받은 꿀팁</h3>
<p>RBF (Random Bit Filp) 시간 때 팀원분께서 인텔리제이 플러그인인 Translation을 알려주셨다. 평소 변수명을 짓기 위하여 구글 번역기를 전전하던 나로써는 너무나 큰 꿀팁이었다.</p>
<p><img src="https://velog.velcdn.com/images/jae_yoon/post/3113f1de-7b15-4b7f-91ec-b5f5b4dc4eeb/image.gif" alt=""></p>
<p>플러그인 설치 이후 명령어는 아래와 같다.</p>
<ul>
<li><code>ctrl + cmd + i</code> : 번역기 팝업 띄우기</li>
<li><code>ctrl + cmd + u</code> : 영어 → 한글 번역 (docs 볼 때 유리)</li>
<li><code>ctrl + cmd + o</code> : 한글 → 영어 번역 (변수명 짓기 개꿀)</li>
</ul>
<br>

<h3 id="계산기-설계-고민">계산기 설계 고민</h3>
<p>설계에 관한 고민만 하다가, 본격적으로 코드를 많이 짜지는 못한 상황이다.</p>
<ul>
<li>Operator 인터페이스를 구현하는 Addition, Substraction, Multiplication, Division은 너무 과한 것 아닐까? 세부적으로 구현되는 코드도 별로 없을 것이고.. 책임을 &quot;연산&quot;으로만 나누고 &quot;덧셈&quot;과 같이 구체적으로 나눠야하나?<ul>
<li>enum으로 따로 빼서 함수형 인터페이스를 사용하면 어떨까</li>
</ul>
</li>
<li>Integer 사용 시, 계산 결과가 21억까지 밖에 나오지 않을텐데, 예외 처리를 하는 것보다 더 큰 수를 다룰 수 있도록 하는 것이 낫지 않을까<ul>
<li>BigDecimal을 사용하면 어떨까</li>
</ul>
</li>
<li>메뉴 번호를 입력 받을 때 1번과 2번만 받아야하는데, 이를 따로 뺴야하나?<ul>
<li>이것 역시 enum으로 빼고 이외의 숫자나 문자가 들어오게 된다면 예외를 던져볼까</li>
</ul>
</li>
<li>예외처리하는 클래스를 따로 만들어서 처리하는 것이 나을까</li>
<li>Console을 통하여 입출력을 받는 상황인데, 나는 Input 인터페이스와 Output 인터페이스를 만들었고 이를 구현하는 InputView 클래스와 OutputView 클래스를 만든 상황. 이걸로 충분할까?</li>
</ul>
<br>

<h3 id="팀-미팅-시간">팀 미팅 시간</h3>
<p>위와 같은 고민만 하다가 정작 코드는 많이 못 짠 상황인데, 팀 미팅 시간에 다른 팀원분은 진행이 어느정도 된 상태라 흑구 멘토님이 간단하게 리뷰해주셨다. 그 과정에서 자연스레 나의 고민도 약간이나마 해결되었다.</p>
<ol>
<li>연산이라는 책임만 지게 하는 것이 괜찮다. 객체지향적으로 설계하다보면, 파일이 너무 많아지는 것도 있고, 사칙연산 하나하나를 클래스로 빼게 되는 것은 좋지 않아 보인다. enum으로 빼보자. (내가 고민하던 방향으로 해결 🟢)</li>
<li><code>calculate(int, int)</code> 같은 경우 테스트 코드를 짰을 때 문제가 생길 것 같지 않을까? 역시 내가 고민하던 방향이 맞았다. BigDecimal을 사용해보자.</li>
<li>메뉴 번호 처리 역시 enum으로 빼는 방향이 맞았다.</li>
<li>예외 처리하는 클래스를 따로 만드는 것 까지는 해결되지 않았지만, 최대한 <strong>자바스럽게</strong> 코드를 짜보자. try-catch나 throw와 같이 예외처리를 반드시 해보는 쪽으로! 코드가 더러워지는건 같이 고민해주겠다.</li>
<li><code>if - else if</code> 에 관하여 생각해본적이 있는가. if는 분기처리를 하여 쭈루룩 다 돌아야하기도 하고 가독성도 그렇게 좋지는 않다. <code>switch</code> 문을 고려하자. <strong><a href="https://sas-study.tistory.com/475">흑구님의 Java Switch문 정리하기</a></strong> 게시글을 참고하자.</li>
<li>Input, InputImpl, Output, OutputImpl, Console이 있고 상속의 형태가 아닌 컴포지션을 이용해보는 것은 어떠한가 (디자인 패턴의 컴포지트 패턴 ❌)</li>
<li>Calculator에서 repository를 다룰 필요가 있나? 또, Calculator가 맡는 역할이 너무 많지 않은가?<ul>
<li>계산기의 책임이 연산의 책임이라고 생각할 필요가 없을 것 같다.</li>
<li>계산기, 연산, 표현식과 같이 3가지 책임으로 생각해보는 것은 어떨까</li>
</ul>
</li>
<li>시스템이 거대해졌을 경우도 생각해보자.<ul>
<li>Main 메서드에서 ManagementSystem과 같이 시스템을 만들고 의존성을 주입해보는 방식</li>
</ul>
</li>
</ol>
<p>우선, 기억 나는대로 다른 팀원분이 받은 피드백을 정리해봤는데, 사실 이해가 가지 않는 부분도 많았다. 이 부분은 내일 추가로 질문드릴 예정이다.</p>
<br>

<h3 id="내가-받은-피드백">내가 받은 피드백</h3>
<p>pre 팀 첫 미팅 때 나왔던 피드백과 사실 결이 비슷했다. 받은 피드백을 토대로, 앞으로 어떤식으로 바꿔가야할 지 목록은 아래와 같다.</p>
<ul>
<li>하나를 깊게 파는 것이 너무 중요하지만, 사실 데브코스 기간 동안은 잠시 내려 놓자. 가성비가 굉장히 떨어지는 방식이다. 결국 목적은 취업이니까.</li>
<li>설계라던가 꼼꼼함이라던가는 조금 내려놓자. 특히, TIL 작성, 수업 필기 등 코드 짜는 것이나 외적인 요소에 너무 신경쓴다. <strong>제발 설계는 간단히, 필기는 간단히하고 코드부터 짜기, 머리로 이해하며 기억하기를 추구하자</strong></li>
<li>데브코스 과정은 생각보다 어렵고 빡센 과정이다. 올빼미형으로 살다가는 버티기 힘들 수 있다. 생활 패턴을 아침형으로 바꿔보자.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드 데브코스 TIL 4기 - 06.07(수)]]></title>
            <link>https://velog.io/@jae_yoon/%EB%B0%B1%EC%97%94%EB%93%9C-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-TIL-4%EA%B8%B0-06.07%EC%88%98</link>
            <guid>https://velog.io/@jae_yoon/%EB%B0%B1%EC%97%94%EB%93%9C-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-TIL-4%EA%B8%B0-06.07%EC%88%98</guid>
            <pubDate>Wed, 07 Jun 2023 17:19:39 GMT</pubDate>
            <description><![CDATA[<h2 id="💻-2주차-day-03">💻 2주차 DAY 03</h2>
<p>2주차 DAY 03 ~! 강의를 2일치 몰아들었다. 모던 자바의 컬렉션과 스트림에 관한 이야기와 본격적인 객체지향 설계를 학습했다. 특히, 함수형 인터페이스를 적용하는 부분에서 곰튀김님께서 설명을 너무너무 잘해주셔서 아하 모먼트가 많았고 수업을 재밌게 들었다. 마지막으로, 본격적인 계산기 과제를 위하여 간단하게 설계해보는 시간을 가졌다.</p>
<br>

<h3 id="collection">Collection</h3>
<p>사실 JAVA가 약한 나는 컬렉션이 친숙하지 않았다. 단순히 자료구조라고만 생각하고 있었는데, 조금 더 이해해보면 <strong>여러 데이터의 묶음이 컬렉션</strong>이라고 이해할 수 있다.</p>
<ul>
<li>데이터 덩어리를 <strong>받아서</strong>, 데이터 덩어리를 <strong>내보내자</strong>.</li>
<li>컬렉션은 추상체-구상체의 구조를 띈다.<ul>
<li>Collection (추상체)<ul>
<li>List (Colleciton의 구상체)<ul>
<li>LinkedList (List의 구상체)</li>
<li>ArrayList (List의 구상체)</li>
<li>Vector (List의 구상체)</li>
<li>Stack (List의 구상체)</li>
</ul>
</li>
<li>Set<ul>
<li>HashSet</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<h3 id="함수형-인터페이스-적용">함수형 인터페이스 적용</h3>
<p>이전 시간에 JAVA API에서 제공해주는 함수형 인터페이스에 관하여 배우기는 하였지만, 어떤식으로 적용되는지 사실 와닿지 않았었다. 오늘 컬렉션에 적용하는 모습을 보면서 살~짝 느낌을 잡았다.</p>
<ul>
<li>함수형 인터페이스를 활용하여 고정된 형태의 기능을 가지는 것이 아니라, <strong>사용하는 측에서 그 기능을 정의할 수 있는 범용적인 형태의 컬렉션을 만들어 냄</strong></li>
<li>이렇게 여러 데이터를 묶어서 다루는 것을 <strong>Collection</strong>이라고 한다. Collection은 데이터의 묶음이다. filter를 하든 map을 하든 묶여있는 데이터 덩어리를 내보냄</li>
</ul>
<br>

<p><strong>예제 1. forEach 만들기 + consumer 적용</strong></p>
<pre><code class="language-java">public void foreach() {
    for (int i = 0; i &lt; list.size(); i++) {
        T data = list.get(i);
    }
}</code></pre>
<ul>
<li><code>foreach()</code> 메서드는 data에서 무언가를 하려고 하는데, return 없이 void인 상태이다. 함수형 인터페이스의 <strong>consumer</strong> !?</li>
</ul>
<pre><code class="language-java">public void foreach(Consumer&lt;T&gt; consumer) {  
    for (int i = 0; i &lt; list.size(); i++) {  
        T data = list.get(i);  
        consumer.accept(data);  
    }  
}</code></pre>
<ul>
<li>consumer를 사용해서 accept로 받아주는 모습이다.</li>
</ul>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) {
        // 익명 클래스
        new MyCollection&lt;&gt;(Arrays.asList(1, 2, 3, 4, 5))  
            .foreach(new Consumer&lt;Integer&gt;() {  
                @Override  
                public void accept(Integer integer) {  
                    System.out.println(integer);  
                }  
            });

        // 람다식
        new MyCollection&lt;&gt;(Arrays.asList(1, 2, 3, 4, 5, &quot;A&quot;))
            .foreach(System.out::println);
    }
}</code></pre>
<ul>
<li>익명 클래스 사용과 람다식의 사용이 익숙하지 않아서 만들어본 <code>foreach()</code> 메서드를 사용하는 예제도 넣어봤다.</li>
</ul>
<br>

<p><strong>예제 2. 데이터 변형해보기 + function 적용</strong></p>
<pre><code class="language-java">public &lt;U&gt; MyCollection&lt;U&gt; map(Function&lt;T, U&gt; function) {  
    List&lt;U&gt; newList = new ArrayList&lt;&gt;();  
    foreach(data -&gt; newList.add(function.apply(data)));  
    return new MyCollection&lt;&gt;(newList);  
}</code></pre>
<ul>
<li>데이터를 변경하는 것을 매핑한다 혹은 map 과 같이 표현함</li>
<li><code>public MyCollection&lt;U&gt; map(Function&lt;T, U&gt; function)</code> 이라고 하면 오류 발생<ul>
<li>1번에서 정의한 <code>MyCollection</code>에는 <code>&lt;T&gt;</code>만 있음</li>
<li>이 map 메서드에서만 사용하는 <code>&lt;U&gt;</code> 타입에 대하여 추가해야함</li>
<li>그래서 <code>public &lt;U&gt; MyCollection&lt;U&gt; map(Function&lt;T, U&gt; function)</code> 형태가 만들어짐</li>
</ul>
</li>
<li>newList에는 List에 들어있는 T 타입의 값이 <strong>function이라는 함수형 인터페이스</strong>에 의해 새로운 U 타입으로 바뀐 결과가 들어감</li>
</ul>
<br>

<p><strong>예제 3. 필터 만들기 + Predicate 적용</strong></p>
<pre><code class="language-java">public MyCollection&lt;T&gt; filter(Predicate&lt;T&gt; predicate) {  
    List&lt;T&gt; newList = new ArrayList&lt;&gt;();  
    foreach(data -&gt; {  
        if (predicate.test(data)) newList.add(data);  
    });  
    return new MyCollection&lt;&gt;(newList);  
}</code></pre>
<ul>
<li>파라미터가 있고 그에 대한 boolean 값을 반환해주는 <strong>predicate</strong> 사용</li>
<li>결과가 참이면 newList에 추가되도록~</li>
</ul>
<br>

<h3 id="iterator">Iterator</h3>
<p>Colleection은 데이터 덩어리를 받아서, 데이터 덩어리를 내보낸다고 하였다. 이러한 여러 데이터의 묶음을 풀어서 하나씩 처리하고 싶은 경우가 있는데, 이러한 수단을 제공하는 것이 Iterator이다. <code>next()</code>를 통해 순방향으로 데이터를 조회하는 것은 가능하지만, 역순으로 움직일 수는 없다.</p>
<p>정리해보면 Iterator는 <strong>데이터 덩어리인 Collection을 풀어서 하나씩 사용하는 형태</strong>라는 것이다. 하지만, 이렇게 되면서 데이터 묶음으로 처리하는 Collection에서 사용하던 <strong>편리한 기능인 고차함수 map, filter, reduce</strong> 같은 기능을 사용하지 못하게 되었다.</p>
<p>그러면, Iterator 같이 하나씩 풀어쓰는 형식에서는 함수형 인터페이스를 활용한 고차함수를 사용할 수 없을까?</p>
<br>

<h3 id="stream">Stream</h3>
<p>그래서 등장한 것이 Stream이다. Stream은 <strong>데이터의 연속으로, 연속된 데이터 중 하나</strong>를 의미한다. 내가 이야기할 것은 모던 자바의 Stream이다. IO 스트림도 있지만, 그것은 <strong><a href="https://shin-jae-yoon.github.io/brain/Lecture/fun-java/fun-java09#io-stream">Dev Uni 기록용 블로그 - IO 스트림</a></strong> 을 참고하자.</p>
<p>모던 자바에 추가된 스트림은 데이터 스트림으로, 제공되는 <code>Collections.stream()</code>을 의미한다. <strong>컬렉션 안의 데이터를 연속적인 흐름으로 처리할 수 있게 되었다.</strong> 추가로, 고차함수 <code>filter</code>, <code>map</code>, <code>forEach</code>가 제공되어 아주 편하다.</p>
<blockquote>
<p>⭐️ Stream의 등장으로 인하여, 연속된 데이터에 대한 풍부한 고차함수들을 사용하여 강력한 기능을 간결하게 표현할 수 있게 되었다 !!</p>
</blockquote>
<br>

<p>Stream을 생성하는 방식에는 <strong>generate</strong>와 <strong>iterate</strong>가 있다.</p>
<ul>
<li><strong><code>Stream.generate()</code></strong><ul>
<li><code>generate()</code>의 파라미터로 supplier가 들어감</li>
<li>함수형 인터페이스 supplier는 input 값은 없고 결과값이 무언가 나와야하는 것을 의미한다.</li>
</ul>
</li>
</ul>
<pre><code class="language-java">Random r = new Random();  
Stream.generate(() -&gt; r.nextInt())  
    .limit(10)  
    .forEach(System.out::println);</code></pre>
<br>

<ul>
<li><strong><code>Stream.iterate()</code></strong><ul>
<li>초기 값인 시드값이 들어오고, 그 다음에 어떠한 결과를 전달하는 방식</li>
<li>파라미터 2개를 이용한다.</li>
</ul>
</li>
</ul>
<pre><code class="language-java">Stream.iterate(0, (i) -&gt; i + 1)  
    .limit(20)  
    .forEach(System.out::println);</code></pre>
<br>

<h3 id="optional">Optional</h3>
<p>개발자로 살아가며 가장 많이 마주할 문제가 바로 NPE (Null Pointer Exception)이다. Java는 거의 모든 것이 reference value라서 null이 될 가능성을 항상 가지고있다. 그래서 매번 null 체크를 확인해야할 필요가 있었고, 이러한 Optional이 등장하게 되었다.</p>
<blockquote>
<p>여담이지만, 예전에 공부했던 바로는 사실 Optional을 사용하지 않는 상황이 제일 좋다고 한다. 즉, null이 나올 것 같다고 무조건 Optional을 때려버리는 것이 아니라 최대한 방어적으로 코딩하는 것이 좋다고 한다.</p>
</blockquote>
<br>

<p><strong>Optional은 null이 될 수 있는 값들을 운반해주는 일종의 캐리어</strong>이다. 즉, Optional이 비어있는지 아닌지를 체크하여 NPE를 피하면서 null인지 아닌지를 알 수 있는 것이다.</p>
<ul>
<li><code>isEmpty()</code> -&gt; 값이 없으면 true ( = null이면 true )</li>
<li><code>isPresent()</code> -&gt; 값이 있으면 true</li>
</ul>
<br>

<h3 id="숫자야구-프로젝트">숫자야구 프로젝트</h3>
<p>실습 프로젝트 수업에서는 숫자야구를 구현해보는 방식이었다. 내가 봐왔던 숫자야구 구현과는 다르게 곰튀김님께서는 함수형 인터페이스를 자유자재로 다루셨고, IDE의 기능을 최대한 활용하여 상황에 맞게 코드를 짜시는 것을 보고 감탄했다.</p>
<p>코드를 이해하기는 했는데, 생각의 흐름이 너무 좋았어서, 이는 강의와 코드를 여러 번 반복해서 보려고 한다. 완벽하게 내 것으로 만들고 나중에 따로 포스팅하는 것을 추가하도록 하겠다.</p>
<br>

<h3 id="계산기-프로젝트-preview">계산기 프로젝트 preview</h3>
<br>

<p><strong>프로젝트 세팅 과정</strong></p>
<p>오늘은 프로젝트 세팅과 간단한 설계에서 끝났다. 프로젝트 세팅에 관해서 조금 애먹었는데, 먼저 fork 뜨고 clone 이후 작업 branch를 하나 만들고 <code>.gitignore</code> 추가까지는 평탄했다. </p>
<p>그러나, 프로젝트를 생성하려고 보니 클론한 프로젝트에는 README.md 파일 밖에 없어서 gradle을 추가로 설정해야하는 상태였다. New Project를 하려고 하니 새로운 폴더가 계속 형성되었고 <code>java-calculator/java-calculator</code> 이런식으로 되니까 보기가 불편했다.</p>
<p>그래서, 1주차에 학습했던 것을 토대로 터미널로 프로젝트 폴더에 들어가서 <code>gradle init</code>으로 환경을 구성했다. 기존에 인텔리제이로 new project 하던 것보다 더 많은 파일이 나왔고, <code>build.gradle</code>에도 하나하나 주석이 달려있어서 사실 보기 좀 불편했다.</p>
<p>그러던 와중 데브코스 팀원께서 방법을 찾으셔서 그 방법으로 시도했다.</p>
<ol>
<li>클론한 프로젝트 폴더에서 new project로 내부에 프로젝트 그대로 생성</li>
<li>생성한 프로젝트 파일을 그대로 밖으로 가져와서 복사 붙혀넣기</li>
<li>인텔리제이 껐다가 키고 gradle 싱크 맞추기</li>
</ol>
<p>이렇게하니 기존에 인텔리제이로 만들던 것과 동일하여서 드디어 기쁘게 시작할 수 있었다...!! 아래는 gitignore 세팅과 추가적인 IDE 세팅들이다.</p>
<br>

<ul>
<li>gitignore</li>
</ul>
<pre><code class="language-bash">HELP.md
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**
!**/src/test/**

### macOS ###
.DS_Store

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/

### VS Code ###
.vscode/
# Ignore Gradle build output directory
build

# Ignore Gradle GUI config
gradle-app.setting

# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar

# Avoid ignore Gradle wrappper properties
!gradle-wrapper.properties

# Cache of project
.gradletasknamecache

### Gradle Patch ###
# Java heap dump
*.hprof</code></pre>
<ul>
<li>인텔리제이 세팅</li>
</ul>
<ol>
<li>File → Project Structures → SDK 자바 버전 11 확인</li>
<li>Settings → Build → Build Tools → Gradle → 둘다 IntelliJ Idea로 수정</li>
<li>Settings → Tools → Actions on Save → Optimize imports 체크</li>
<li>Settings → Editor → General → Auto Import → 가운데 2개 import fly 체크</li>
<li>Settings → Editor → Code Style → Java → 숫자 2개 999로 수정</li>
</ol>
<br>

<p><strong>계산기 설계</strong></p>
<p>아직 디테일하게 설계는 못했지만, 최대한 SOLID 원칙을 신경쓰며 객체지향적으로 구성하려고 노력해보고 있는 상태이다. 지금까지 생각해본 구조는 대략 아래와 같다.</p>
<ul>
<li>View<ul>
<li>Console</li>
</ul>
</li>
<li>Model<ul>
<li>Calculation (인터페이스, 추상체)<ul>
<li>Plus (구상체)</li>
<li>Minus (구상체)</li>
<li>Divide (구상체)</li>
<li>Multiple (구상체)</li>
</ul>
</li>
<li>Priority</li>
</ul>
</li>
<li>Repository<ul>
<li>InMemoryRepository (인터페이스, 추상체)<ul>
<li>MapMemory (구상체)</li>
</ul>
</li>
</ul>
</li>
<li>Controller</li>
</ul>
<p>음~! 근데 각각 계산 방식에 대하여 저렇게 동사는 메서드니까 저걸 구현하는 것으로 가는게 아니라 연산자와 피연산자 객체를 설정하고 그에 해당하는 계산을 따로 빼야하나 싶기도 하고 ,, 컨트롤러는 어떻게 모델과 뷰를 연결시킬지에 대한 고민도 해봐야 할 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드 데브코스 TIL 4기 - 06.06(화)]]></title>
            <link>https://velog.io/@jae_yoon/%EB%B0%B1%EC%97%94%EB%93%9C-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-TIL-4%EA%B8%B0-06.06%ED%99%94</link>
            <guid>https://velog.io/@jae_yoon/%EB%B0%B1%EC%97%94%EB%93%9C-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-TIL-4%EA%B8%B0-06.06%ED%99%94</guid>
            <pubDate>Tue, 06 Jun 2023 14:50:22 GMT</pubDate>
            <description><![CDATA[<h2 id="💻-2주차-day-02">💻 2주차 DAY 02</h2>
<p>2주차 DAY 02 ! 드디어 오늘 부산 방이 빠졌다. pre 팀 기간 동안은 계산기 과제도 있고 여러 집중이 필요해서 서울에 방 구하러 올라가는 것은 pre 팀 기간 이후이지 않을까싶다. 오늘부터는 본격적으로 객체지향적인 설계를 위해 Java를 학습하는 시간이었다.</p>
<br>

<h3 id="interface">Interface</h3>
<p>평소, 추상 클래스와 인터페이스에 관한 차이를 공부한 편이었는데 정작 인터페이스의 기능에 관하여 간략하게만 알고 있었어서, 오늘 강의를 통해 정리해보는 시간을 가지게 되었다.</p>
<p>내가 기존에 일던 인터페이스는 <strong>메뉴얼, 설명서</strong>의 느낌이었다. 인터페이스에 정의해둔 메서드를 구현하는 쪽에서 반드시 구현하도록 강제하는 것이다. 즉, <strong>추상체에 정의된 메서드를 구상체에서 반드시 이것만은 구현해야 한다는 의미이다.</strong> 제작자가 인터페이스를 만들면서 제작자의 의도를 포함시켜 사용자에게 인지시켜준다는 의미로도 생각할 수 있다.</p>
<br>

<p><strong>Interface 기능</strong></p>
<ol>
<li>구현을 강제</li>
<li>다형성 제공</li>
<li>결합도 낮추는 효과</li>
</ol>
<br>

<h3 id="예시를-통해-알아보자">예시를 통해 알아보자</h3>
<p>가장 많이 사용하는 &quot;로그인 기능&quot;에 관하여 이야기 해보자.
예시 코드는 굉장히 간단한 수준이다.</p>
<ul>
<li>구상체에서 추상체인 인터페이스의 메서드를 구현해준 모습</li>
</ul>
<pre><code class="language-java">// 추상체 
public interface Login {
    void login();
}

// 구상체
public class KakaoLogin implements Login {
    @Override
    public void login() {
        System.out.println(&quot;카카오 로그인&quot;);
    }
}

// 구상체
public class NaverLogin implements Login {
    @Override
    public void login() {
        System.out.println(&quot;네이버 로그인&quot;);
    }
}</code></pre>
<br>

<p>그렇다면, 사용 쪽에서 고려할 것들을 생각해보자.</p>
<ol>
<li>인스턴스 생성 시 구상체 타입이 아닌 <strong>추상체 타입으로 선언</strong>하자.<ul>
<li>카카오 로그인과 네이버 로그인에 강하게 결합된 것을 없애기 위해</li>
</ul>
</li>
<li><strong>팩토리 메서드 패턴</strong>을 이용하자.<ul>
<li>사용하는 곳의 구현부를 바꾸지 않고, 팩토리에 정의된 메서드만 수정하기 위해</li>
</ul>
</li>
<li><strong>enum 타입</strong>도 고려해보자.<ul>
<li>enum으로 빼고 팩토리 메서드 패턴에 주입하는 식으로</li>
</ul>
</li>
<li>⭐️ <strong>결정을 호스트 코드로 미루자.</strong> ( = 위임하자 )<ul>
<li>의존체를 외부로부터 주입받아서 최종적으로 동작을 결정하는 것</li>
</ul>
</li>
</ol>
<br>

<p>고려한 내용의 핵심은 <strong>인터페이스 타입(=추상체의 타입)을 사용하여, 인터페이스의 다형성을 이용하면 어떤 것이 들어오든 상관 없이 호스트 코드 쪽에서 로그인을 요청하게 만들 수 있다는 의미</strong>이다.</p>
<p>추상체인 Login 인터페이스를 KakaoLogin과 NaverLogin에서 이용하여 기능을 수행하고 있는 상태이기 때문에 Login에 의존하고 있는 상태이다. 여기에서 의존체를 외부로부터 주입받는다고 하였는데, 왜 주입 받는지 아래를 보자.</p>
<pre><code class="language-java">private Login login = new KakaoLogin();</code></pre>
<p>직접 생성하는 형태를 이용한다면, KakaoLogin이라는 특정 기능을 수행하는 로그인 밖에 하지 못한다. 즉, 의존성을 외부에 맡김으로 인하여, 여러 로그인을 수행할 수 있는 능력을 탑재하는 것이다. 이를 <strong>의존도를 낮춘다</strong>라고 표현한다. 의존성을 주입받는다고 하여 이것을 <strong>의존성 주입, DI (Dependency Injection)</strong>라 한다.</p>
<blockquote>
<p>구상체에 의존 =&gt; 강한 결합
추상체에 의존 + 의존성 주입받음 =&gt; 결합도 낮아짐</p>
</blockquote>
<br>

<h3 id="dip">DIP</h3>
<p>이 과정을 그림으로 살펴보자. 먼저, 의존성을 주입받지 않고 직접 의존하는 형태이다. 예를 들어, UserService에서 비즈니스 로직인 KakaoLogin, NaverLogin에 직접 의존하는 형태이다.</p>
<p><img src="https://velog.velcdn.com/images/jae_yoon/post/236b0c53-8720-4d7f-9164-f1877fdf23d7/image.png" alt=""></p>
<p>특정 기능을 하는 로그인에 강하게 결합되어있다. 만약 구글 로그인이 추가된다면 따로 GoogleLogin 클래스를 만들고 UserService에서 관련 코드를 추가해야한다. 벌써 머리 아프다.</p>
<p><strong>추상체에 의존하면서 외부로부터 의존성을 주입받아보자.</strong></p>
<p><img src="https://velog.velcdn.com/images/jae_yoon/post/288b4b80-9a48-4cb5-9a5d-64deaf31de72/image.png" alt=""></p>
<p>인터페이스를 통하게 되면서, 특정 기능을 수행하는 로그인 측의 화살표 방향이 바뀌게 된 것을 볼 수 있다. 의존성을 외부로부터 주입받는 형태가 되어 이것이 <strong>의존성의 역전, DIP (Dependency Inversion Principle)</strong>이다.</p>
<br>

<p><strong>DIP (Dependency Inversion Principle)</strong></p>
<ul>
<li>의존관계 역전 원칙</li>
<li>상위 레벨의 모듈은 절대 하위 레벨 모듈에 의존 ❌<ul>
<li>둘다 <strong>추상화에 의존해야 한다.</strong></li>
</ul>
</li>
<li>클래스 간 <strong>결합을 느슨하게 하기 위함</strong></li>
<li>클래스 <strong>변경에 따른 클래스들의 영향을 최소화하기 위함</strong></li>
</ul>
<br>

<h3 id="default-메서드">default 메서드</h3>
<p>인터페이스에 정의된 추상 메서드는 반드시 구현해야 한다는 엄격함으로 인하여, Java 8 이후부터 default 메서드가 등장하게 되었다. 이는 간단하게 <strong>인터페이스가 구현체를 가질 수 있게 된 것</strong>이다.</p>
<p>default 메서드가 없던 시절을 예시로 떠올려서 생각해보자. 인터페이스에 3가지 추상메서드가 정의되어 있지만, 나는 단 1가지만 사용하고 싶은 경우, 무조건 3가지를 반드시 구현해야한다.</p>
<p>이러한 경우 <strong>Adapter (어댑터)</strong>를 이용하는 방식을 고려할 수 있다.</p>
<pre><code class="language-java">public class MyAdapter implements MyInterface {
    @Override
    public void method1() {}

    @Override
    public void method2() {}

    @Override
    public void method3() {}
}

class HelloClass extends MyAdapter {
    @Override
    public void method1() {
        System.out.println(&quot;Hello World!&quot;);
    }
}</code></pre>
<ul>
<li>어댑터에서 인터페이스를 구현한 이후 사용처에서 어댑터를 상속받도록 !</li>
<li>그러나, Java는 다중상속을 지원하지 않기 때문에 안되는 상황 발생</li>
</ul>
<br>

<p>default 메서드 등장으로 인하여 이러한 Adapter 역할을 하게 되었다. default 메서드에 구현하게 되면, <strong>인터페이스 추가만으로 기능을 확장할 수 있다는 장점</strong>도 가지게 되었다. 이러한 특징을 이용하면 인터페이스 분리 원칙인 <strong>ISP (Interface segregation principle)</strong>를 이용하여 객체지향적인 설계도 가능해진다.</p>
<br>

<h3 id="추상-클래스-vs-인터페이스">추상 클래스 vs 인터페이스</h3>
<p>그러면, default 메서드의 등장으로 추상 메서드를 <strong>포함</strong>하던 추상 클래스와 추상 메서드 <strong>만으로</strong> 이루어져 있던 인터페이스의 경계가 모호해진 것 같다. 하지만, Java에서 추상 클래스와 인터페이스를 구분하게 된 이유가 <strong>사용 목적</strong>에 있기 때문에, 이를 고려하면 좋다.</p>
<p>맨 위에서 언급했듯이 <strong>인터페이스는 메뉴얼, 설계도의 느낌</strong>이다. 인터페이스를 구현하는 측에서 같은 동작을 한다는 것을 보장하기 위함이다. KakaoLogin 이든 NaverLogin이든 결국 &quot;로그인&quot;이라는 동작을 하는 것이 보장된다. DB에 접근하는 기술 관련 repository도 살펴보자. JDBC, MyBatis, JPA 무엇을 이용하든 결국 DB에 접근한다는 동작은 보장된다.</p>
<p>그렇다면 추상 클래스는 어떠한가. <strong>추상 클래스는 상속하여 공통된 기능을 만들고, 그를 기반으로 확장해 나가는 느낌</strong>이다. 설계도를 통하여 공통된 동작을 보장하는 느낌과는 다르게 추상 클래스는 부모를 기반으로 자식을 복제하고 거기에서 확장해가는 것이다.</p>
<br>

<h3 id="함수형-인터페이스">함수형 인터페이스</h3>
<p>인터페이스에서 default 메서드가 추가되면서, 인터페이스는 <strong>static 메서드</strong>도 가질 수 있게 되었다. static 메서드는 JVM에 올라갈 때 맨 처음에 로드되므로, 객체 생성 없이 인터페이스의 static 메서드를 호출할 수 있게 되면서, <strong>인터페이스 자체가 함수 제공자가 되었다</strong>는 의미이다. 정의만이 아닌 구현을 통해 기능 자체를 제공할 수 있게된 것이다.</p>
<blockquote>
<p><strong>잠깐, 왜 메서드가 아니라 함수?</strong></p>
<p>클래스에 종속된 함수를 메서드라고 하는데, 인터페이스에서 static 메서드가 추가됨으로 종속되지 않은 상태에서 사용 가능하여 <strong>함수</strong>라고 불리게 되었다. 즉, 함수형 인터페이스 기능이 추가된 것이다.</p>
</blockquote>
<br>

<p>이러한 배경을 바탕으로, 모던 자바에는 함수형 인터페이스가 추가되었다. 함수형 인터페이스는 <strong>추상 메서드가 단, 1개만 존재하는 인터페이스</strong>이다. 편의를 위하여 추가된 함수형 인터페이스는 <code>java.util.function</code> 에 정의되어 있다. 오라클 공식 문서나 <strong><a href="https://hudi.blog/functional-interface-of-standard-api-java-8/">hudi blog - 함수형 인터페이스</a></strong>를 참고하도록 하자.</p>
<pre><code class="language-java">@FunctionalInterface
public interface MyRunnable {
    void run();
}

public class Main {
    public static void main(String[] args) {

        // 오류 !
        MyRunnable my = new MyRunnable();
        my.run();
    }
}</code></pre>
<ul>
<li>그러나, 인터페이스는 인스턴스 생성해서 만들 수 없으니 위 코드는 오류 발생</li>
<li>추상 메서드 1개만 있어서 동작이 예측 가능한데.. 방법이 없을까?해서 나온게 익명 클래스</li>
</ul>
<br>

<h3 id="익명-클래스와-람다-표현식">익명 클래스와 람다 표현식</h3>
<p>익명 클래스의 등장으로 인터페이스의 인스턴스를 생성하고 구현을 바로 정의하게 되었다. 위의 코드를 익명 클래스를 사용하여 수정하면 아래와 같다.</p>
<pre><code class="language-java">@FunctionalInterface
public interface MyRunnable {
    void run();
}

public class Main {
    public static void main(String[] args) {

        MyRunnable my = new MyRunnable() {
              @Override
              public void run() {
                  System.out.println(&quot;안뇽 난 익명클래스&quot;);
              }
        };
    }
}</code></pre>
<p>하지만, <code>new</code>를 사용하여 인스턴스를 생성, <code>@Override</code>를 이용한 메서드 오버라이딩 등 너무나 당연한 코드가 중복되는 것을 없애고자 <strong>Lambda 표현식</strong>이 나오게 되었다.</p>
<pre><code class="language-java">// 익명 클래스
MyRunnable my = new MyRunnable() {
    @Override
    public void run() {
        System.out.println(&quot;안뇽 난 익명클래스&quot;);
    }
};

// 람다 표현식
MyRunnable my = () -&gt; System.out.println(&quot;안뇽 난 익명클래스&quot;);</code></pre>
<br>

<h3 id="메서드-레퍼런스">메서드 레퍼런스</h3>
<p>람다식을 사용하다보면, 인텔리제이에서 노란색 줄이 그어지면서 &quot;제발 나 좀 바꿔줘!&quot;라고 소리쳤다. 그래서 바꿔보면 기괴한 형식이 나왔다.</p>
<pre><code class="language-java">// 람다식
MyMapper&lt;String, Integer&gt; m = (str) -&gt; str.length();
// 메서드 레퍼런스
MyMapper&lt;String, Integer&gt; m = String::length;

// 람다식
MyMapper&lt;Integer, String&gt; m3 = i -&gt; Integer.toHexString(i);
// 메서드 레퍼런스
MyMapper&lt;Integer, String&gt; m3 = Integer::toHexString;</code></pre>
<p>이는, 람다식에서 입력되는 값을 변경없이 바로 사용하는 경우에는, 자명하기 때문에 더 간단한 형태로 바꿔주는 방식이다. 최종으로 적용될 메서드의 레퍼런스를 지정해주는 표현 방식으로써, <strong>메서드 레퍼런스</strong>라고 한다.</p>
<p>메서드 레퍼런스 사용 시 장점이 있는데, <strong>값의 변경이 확실하게 없다고 보장</strong>할 수 있다는 의미이다. 좀 확대해서 표현하면 <strong>입력 값을 변경하지 말라</strong>라는 표현 방식이기도 하다. 코드를 유지보수 해야하는 상황에 메서드 레퍼런스로 작성된 것을 보고 결과값을 마음대로 바꾸지 못하게 하여 안정성을 얻을 수 있다는 장점이 있다.</p>
<br>

<h3 id="모던-자바-사용법">모던 자바 사용법</h3>
<p><code>java.util.function</code>에서 제공해주는 함수형 인터페이스와 람다 표현식을 잘 이용하면 재밌게 코드를 작성할 수 있다.</p>
<pre><code class="language-java">import java.util.function.Consumer;
import java.util.function.Predicate;

public class Main {
    // 여기가 호스트 코드 !
    public static void main(String[] args) {
        new Main().filteredNumbers(
                50,
                i -&gt; i % 5 == 0,
                System.out::println
        );
    }

    void filteredNumbers(int max, Predicate&lt;Integer&gt; predicate, Consumer&lt;Integer&gt; consumer) {
        for (int i = 0; i &lt; max; i++) {
            if (predicate.test(i)) consumer.accept(i);
        }
    }

    void loop(int n, MyConsumer&lt;Integer&gt; consumer) {
        for (int i = 0; i &lt; n; i++) {
            consumer.consume(i);
        }
    }
}</code></pre>
<ul>
<li>함수형 인터페이스인 Consumer를 이용하여 입력은 있지만, 출력은 따로 없어도 되는 상태를 만들었다.</li>
<li>수행해야 할 것을 만들어놓고 구체적인 상황을 직접 처리하는 것이 아닌 <strong>호스트 코드로 미룬 상태</strong>이다. 위에서 의존성을 주입받던 상황과 닮지 않았는가?</li>
<li>결과적으로 실질적인 구현부는 하나도 바꾸지 않으면서 호출하는 쪽에서 기능을 수행하도록 만들 수 있는 것이다. 👍</li>
</ul>
<br>

]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드 데브코스 TIL 4기 - 06.05(월)]]></title>
            <link>https://velog.io/@jae_yoon/%EB%B0%B1%EC%97%94%EB%93%9C-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-TIL-4%EA%B8%B0-06.05%EC%9B%94-x4t1nr9a</link>
            <guid>https://velog.io/@jae_yoon/%EB%B0%B1%EC%97%94%EB%93%9C-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-TIL-4%EA%B8%B0-06.05%EC%9B%94-x4t1nr9a</guid>
            <pubDate>Mon, 05 Jun 2023 14:50:05 GMT</pubDate>
            <description><![CDATA[<h2 id="💻-2주차-day-01">💻 2주차 DAY 01</h2>
<p>2주차 DAY 01부터 본격적으로 과제가 나오며 바쁜 일상이 시작되겠다는 것이 느껴졌다. 아직 부산에 방이 빠지지 않고 서울에 방을 구하지 못한 상황이고, 개인사로 인하여 심리적으로 안정되지 못한 상태이기 때문에, 공부에 풀 집중이 안되는 것이 아쉽다. 하지만, 현재 상황에 맞춰서 최대한 집중해보려고 한다 :)</p>
<br>

<h3 id="객체지향-프로그래밍">객체지향 프로그래밍</h3>
<p>평소, JAVA를 사용해왔기에 나는 객체지향 프로그래밍을 한다고 생각했지만, 오늘 강의를 통해 스스로 의문을 가지게 되었다. 나는 과연 객체지향적인 특성을 잘 활용하며 코드를 짜고 있을까? 결국 <strong>객체지향 프로그래밍을 한다는 것은 좋은 객체를 잘 만들고 있는가</strong>를 의미한다고 생각했다. <strong>프로그램이 거대화</strong>하면서 등장하게 된 객체지향 프로그래밍이라는 패러다임에 따른다면, 좋은 객체와 나쁜 객체는 아래와 같다.</p>
<ul>
<li><strong>좋은 객체 : 역할과 책임에 충실하면서 다른 객체와 잘 협력하여 동작하는 객체</strong></li>
<li>나쁜 객체 : 여러가지 역할을 한 가지 객체에게 부여하거나, 이름과는 맞지 않는 속성과 기능을 가지도록 하거나, 제대로 동작하지 않는 객체. 또한 다른 객체와 동작이 매끄럽지 않는 것도 나쁜 객체</li>
</ul>
<br>

<h3 id="pojo와-psa">POJO와 PSA</h3>
<p>좋은 객체를 생각하며, 자연스럽게 POJO(Plain Old Java Object)가 떠올랐다. POJO에 관한 자세한 내용은 <strong><a href="https://shin-jae-yoon.github.io/brain/Interview/dog-study/dog-week06/#pojo">Dev Uni 기록용 블로그 - POJO 설명</a></strong> 을 참고하자.</p>
<p>POJO(Plain Old Java Object)는 직역하자면 오래된 방식의 간단한 자바 객체라는 의미이다. 이는 특정 기술에 종속되지 않는 순수한 자바 객체를 의미한다.</p>
<p><strong>객체 지향적인 원리에 충실하면서 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 POJO</strong>라 말하고 POJO에 애플리케이션의 핵심로직과 기능을 담아 설계하고 개발하는 방법을 POJO 프로그래밍이라고 할 수 있다.</p>
<p>예를 들어, ORM(Object Relationship Mapping) 기술을 사용하려면 ORM을 지원하는 프레임워크를 사용해야한다. 만약에 Java 객체가 ORM 기술을 사용하기 위해 Hibernate 프레임워크를 직접 의존하는 순간 POJO라고 할 수 없다. “특정 기술에 종속되었기 때문&quot;이다.</p>
<p>그런데 Java 객체가 ORM 기술을 사용하기 위해 Hibernate 프레임워크를 직접 의존하는 순간 POJO가 아니라고 했으면서, Spring은 어떻게 POJO를 유지하면서 Hibernate를 사용할까? 이는 Spring에서 정한 표준 인터페이스가 있기 때문이다.</p>
<p>ORM을 사용하기 위해 JPA(Java Persistence API)라는 표준 인터페이스를 정의해뒀고, ORM 프레임워크들은 JPA의 구현체가 되어 실행된다. 이것이 새<strong>로운 엔터프라이즈 기술을 도입하면서도 POJO를 유지하는 방법이고 이런 방법을 PSA</strong>라고 한다.</p>
<p><strong>PSA(Portable Service Abstraction)란 환경과 세부 기술의 변화에 관계없이 일관된 방식으로 기술에 접근할 수 있게 해주는 것</strong>을 의미한다. PSA가 적용된 대표적인 예시는 JDBC, JPA, Transaction Manager가 있다.</p>
<p>다시 POJO로 돌아와서, 그렇다면 특정 기술규약과 환경에 종속되지 않으면 모두 POJO라고 말할 수 있을까? 그렇지 않다. <strong>진정한 POJO는 위에서 말한것처럼 객체 지향적인 원리에 충실하면서 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 의미</strong>한다.</p>
<blockquote>
<p>정리하자면, 객체지향적인 원리에 충실하는 좋은 객체이면서 환경과 기술에 종속되지 않고 필요에 따라 재활용 될 수 있는 방식으로 설계된 오브젝트가 POJO이구나 !</p>
</blockquote>
<br>

<h3 id="아키텍처-이야기">아키텍처 이야기</h3>
<p>거대한 프로그램에서 객체가 역할과 책임에 충실하도록 기능을 나눠서 좋은 객체로 만드는 것을 살펴보았다. 그렇다면 이것을 <strong>아키텍처 자체의 관점에서 살펴보자.</strong> 내가 지금까지 코드를 짜면서 해왔던 모든 방식은 모놀리식 아키텍처이다. 모놀리식 아키텍처는 전통의 아키텍처를 지칭하며, 소프트웨어의 모든 구성요소가 한 프로젝트에 통합되어 있는 형태이다.</p>
<p>최선을 다하여 좋은 객체를 만들고 잘게 쪼갰음에도 애플리케이션의 한 프로세스에 대한 수요가 급증하면 아키텍처 전체를 확장해야하는 상황이 왔다고 하자. 모놀리식이니까 scale up 형식으로 서버 스펙을 향상 시키는 것도 극한에 이르렀다고 가정하자. 어떻게 더 쪼개야할까?</p>
<br>

<ol>
<li>MSA (Micro Service Architecture)</li>
</ol>
<p>아마 먼저 떠올린 생각이 아닐까싶다. 모놀리식 아키텍처에서 MSA 아키텍처로 변경하는 것을 생각할 수 있다. 그러나, MSA 아키텍처에 관하여 자세히는 모르지만, 현직에 계신 분들의 의견을 들었을 때 생각보다 MSA에 부정적이신 분들이 많았고 MSA로 쪼개는 것은 그렇게 어울리는 방법이 아닐 수도 있다. 대표적인 단점은 아래와 같다.</p>
<ul>
<li>서비스가 분산되어 있어 관리하기 어려움</li>
<li>트랜잭션 관리, 장애 추적 및 테스트 등이 쉽지 않음</li>
<li>서비스마다 DB가 분리되어 데이터의 조회가 어려움 (당연히 DB 샤딩의 난이도도 급증할 것) + 데이터의 중복 발생</li>
<li>테스트가 불편</li>
<li>서비스 간 호출 시 REST API 사용으로 인한 통신비용, Latency(지연시간) 증가</li>
<li>전체 서비스가 커짐에 따라 복잡도가 기하급수적으로 높아질 수 있음.</li>
</ul>
<p>이 중 가장 큰 단점은 개인적인 생각으로 비용적인 측면에 있지 않나라고 생각한다. DAU가 높지 않은 상황에 무리하게 모놀리식에서 MSA로 전환한다면 비용적인 폭탄을 맞지 않을까... 현업에 발을 디뎌본 적도 없는 취준생이라 개인적인 추측이고 자세한 상황은 모르겠지만 MSA로 전환은 쉽지 않다고 생각한다.</p>
<br>

<ol start="2">
<li>모놀리식을 유지하면서 멀티모듈</li>
</ol>
<p>최초에 애플리케이션을 개발할 때 멀티모듈로 개발한다면, 추후 서비스가 커졌을 때 확장성도 좋고 <strong>응집도는 커지되, 결합도는 낮아진다</strong>라는 이야기를 들었다. 아직, 멀티 모듈에 관하여 지식이 부족한 상태라 장단점을 확실하게 언급할 수는 없지만, 내가 채택을 해야하는 상황이라면 MSA 보다는 이 방식을 채택할 것 같다. 멀티모듈에 관한 자세한 이야기는 <strong><a href="https://techblog.woowahan.com/2637/">우아한 형제들 - 멀티모듈 설계 이야기</a></strong> 를 참고하자.</p>
<br>

<h3 id="객체지향의-특성">객체지향의 특성</h3>
<p>좋은 객체, POJO가 되기 위하여 객체지향적인 원리에 충실해보자. 그러기 위하여 객체지향의 특성을 알아보려고 한다. 사실 개발 공부를 하면서 이런식의 암기는 좋아하지 않는데, 저번에 인터넷에서 보고 바로 외워버려서 말해보려고 한다. 객체지향의 특성은 캡상추다! (캡슐화, 상속, 추상화, 다형성)으로 쉽게 외워보자 ㅋㅋ</p>
<br>

<p><strong>1. 캡슐화</strong></p>
<ul>
<li>완성도가 있다.</li>
<li>정보가 은닉되어있다.</li>
</ul>
<p>좋은 객체가 되려면 객체가 기능을 수행해야 하는 책임을 가진다고 했는데, 이러한 관점에서 완성도가 있다는 의미이다. 캡슐화를 진행하면서 <strong>의존성을 낮추고 결합도를 낮추는 것</strong>이 핵심이다.</p>
<p>캡슐화 과정에서 접근지정자, 접근제한자를 통해 정보은닉이 자연스레 따라온다. 객체의 정보에 밖에서 접근하는 행위를 막는 것이다.</p>
<blockquote>
<p>디자인패턴의 가장 중요한 원칙과도 일맥상통한다. 애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분과 분리하여 나머지 코드에 영향을 주지 않도록 캡슐화하는 점을 의미한다.</p>
</blockquote>
<br>

<p><strong>2. 상속</strong></p>
<ul>
<li>상위 = 부모 = super = <strong>추상 객체</strong></li>
<li>하위 = 자식 = (this) = <strong>구체 객체</strong></li>
</ul>
<p>곰튀김님께서 상속에 관한 오해를 설명해주셨을 때 가슴이 뜨끔했다. 평소 내가 가지던 생각과 완벽하게 일치했기 때문이다. 맹목적으로 공통된 기능이 있다면 상속으로 빼려고 한 안일한 생각이었다.</p>
<ul>
<li>오해 : 공통된 기능을 여러 객체에게 전달하고 싶을 때 상속을 많이 사용</li>
<li>진실 : <strong>추상과 구체 관계에서 상속을 표현해야 한다.</strong></li>
</ul>
<br>

<p><strong>3. 추상화</strong></p>
<ul>
<li>상속에서의 진실은 자연스럽게 추상화의 개념으로 이어진다.</li>
<li>상속관계는 상위로 갈수록 추상화, 하위로 갈수록 구체화 된 모습이다.</li>
<li>추상체 : 추상화된 객체 / 구상체 : 구체적인 객체</li>
<li><strong>객체 간 관계에서 상위에 있는 것이 항상 하위보다 추상적이어야 한다.</strong></li>
</ul>
<p>추상체와 구상체를 만드는 기준에는 크게 3가지 정도가 있다.</p>
<ul>
<li>둘다 구체적인 기능을 가지고 있으며, <strong>의미적으로만 묶은 상태</strong></li>
<li>구현체를 가지지 않고 정의만 하여 <strong>추상클래스 형태로 제공하는 상태</strong></li>
<li>객체 자체가 추상적인 <strong>인터페이스 상태</strong></li>
</ul>
<p>여기에서, 왜 추상체를 가져야할까?에 관한 이유는 자연스럽게 다형성과 연결된다.</p>
<br>

<p><strong>4. 다형성</strong></p>
<ul>
<li>type(형)을 여러 형태로 표현할 수 있다.</li>
<li>즉, 구체 클래스가 아닌 추상 클래스로 표현할 수 있는 상태를 의미한다.</li>
<li>누가 접근하느냐에 따라 필터링 된 기능만 제공하는 형식도 가능하다.</li>
</ul>
<pre><code class="language-java">List&lt;Integer&gt; list = new ArrayList&lt;Integer&gt;();</code></pre>
<p>구체 클래스인 ArrayList를 추상 클래스인 List로 표현할 수 있는 상태이면서, List에 해당하는 기능만 필터링 된 형식으로 제공한다. 즉, ArrayList에만 있는 기능은 위와 같은 코드에서 사용할 수 없다.</p>
<p>다른 예시로는 아래와 같은 로그인이 있다.</p>
<p><img src="https://velog.velcdn.com/images/jae_yoon/post/3afbd311-a21d-401a-962e-c19a9cd8621b/image.png" alt=""></p>
<p>구상체인 카카오 로그인이나 네이버 로그인으로 표현하지 않고 추상체인 로그인 인터페이스로 표현하면서, 접근하는 사람에 따라 필터링 된 기능만 제공하는 형식이 가능하다.</p>
<br>

<h3 id="객체지향-설계-solid">객체지향 설계, SOLID</h3>
<p>캡슐, 상속, 추상화, 다형성 같은 객체지향의 특성을 잘 지키는 좋은 객체가 의미하는 바는 알았다. 그렇다면 <strong>좋은 설계를 했는지는 어떻게 알아야할까?</strong> 이에 대한 답은 아래 2가지이다.</p>
<ul>
<li>객체를 어떻게 구분했는지</li>
<li>객체간의 연관관계가 어떠한지</li>
</ul>
<p>좋은 설계를 하기 위해 객체를 기능에 따라 어떻게 잘 나누고, 어떻게 잘 연관짓느냐에 관한 원칙이 <strong>SOLID 원칙</strong>이다.</p>
<br>

<ul>
<li><strong>S : SRP (Single responsibility principle)</strong><ul>
<li>단일 책임 원칙</li>
<li><strong>수정이 필요할 경우, 수정되는 이유는 하나 때문이어야 한다.</strong></li>
<li>객체를 하나 만들면, 그 객체에다가 책임을 하나만 줘라</li>
<li>책임은 객체가 수행해야 하는 임무, 기능을 의미</li>
<li>그 객체가 가진 기능은 하나만 있으면 된다.</li>
<li>하나의 객체에 너무 많은 기능을 가지게 하지마라</li>
</ul>
</li>
</ul>
<br>

<ul>
<li><strong>O : OCP (Open/Closed principle)</strong><ul>
<li>개방 폐쇄 원칙</li>
<li><strong>수정에는 닫히고, 확장에는 열어라</strong></li>
<li>처음부터 객체를 만들 때, 수정할 수 있는 형태로 만드는 것이 아니라, 확장해서 기능을 변경할 수 있는 방식으로 만들어라.</li>
<li>즉, 기존거 건들지마라</li>
</ul>
</li>
</ul>
<br>

<ul>
<li><strong>L : LSP (Liskov substitution principle)</strong><ul>
<li>리스코프 치환 원칙</li>
<li><strong>추상객체로 사용되는 부분에 구상객체가 들어가도 아무 문제 없어야 한다.</strong></li>
<li>추상체와 구상체의 관계가 명확하다면, LSP는 잘 지켜질 것 이지만, 추상화에서 언급한 상속관계에서 공통적인 기능은 부모에 두는 경우로써 상속을 사용했다면 LSP가 깨지기 쉽다.</li>
</ul>
</li>
</ul>
<br>

<ul>
<li><strong>I : ISP (Interface segregation principle)</strong><ul>
<li>인터페이스 분리 원칙</li>
<li>로그인이라는 인터페이스가 있고 검색이라는 인터페이스로 나눠서 구성한다고 하자. 로그인 기능만 제공해주고 싶으면 로그인 인터페이스로 보낼 수 있고, 검색 기능만 제공해주고 싶으면 검색 인터페이스로 보낼 수 있는 것이니까 인터페이스를 분리하면 유리하다.</li>
</ul>
</li>
</ul>
<br>

<ul>
<li><strong>D : DIP (Dependency inversion principle)</strong><ul>
<li>의존관계 역전 원칙</li>
<li>상위 레벨의 모듈은 절대 하위 레벨 모듈에 의존하지 않는다. ( = 둘 다 추상화에 의존해야 한다 )</li>
<li>결국, 클래스 간 결합을 느슨히 하기 위함이다.  클래스의 변경에 따른 클래스들의 영향을 최소화하는 것이 목적</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드 데브코스 TIL 4기 - 06.02(금)]]></title>
            <link>https://velog.io/@jae_yoon/%EB%B0%B1%EC%97%94%EB%93%9C-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-TIL-4%EA%B8%B0-06.02%EA%B8%88</link>
            <guid>https://velog.io/@jae_yoon/%EB%B0%B1%EC%97%94%EB%93%9C-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-TIL-4%EA%B8%B0-06.02%EA%B8%88</guid>
            <pubDate>Fri, 02 Jun 2023 12:02:16 GMT</pubDate>
            <description><![CDATA[<h2 id="💻-1주차-day-02">💻 1주차 DAY 02</h2>
<p>1주차 DAY 01인 06월 01일(목)에는 슬랙과 노션에 초대되었고, 간단한 OT를 진행하며 앞으로 데브코스 과정의 방향성에 대해 자세하게 알게 되었고, 2주간 진행될 Pre팀의 팀원들과 간단하게 인사하며 하루를 마무리 했었다. 오늘부터 본격적으로 프레임워크를 위한 JAVA 학습이 시작되었다. 기본적인 개념은 알고 있다는 전제하에 조금 더 심화적인 부분에 관하여 수업이 진행되었다. 기존에 알고 있던 내용들을 리마인드하는 시간이었다.</p>
<br>

<h3 id="java-개발환경">JAVA 개발환경</h3>
<ul>
<li>JDK, JRE에 관한 간단한 설명</li>
<li><strong><a href="https://jae-yoon.tistory.com/15">Dev Uni 기술블로그 - Java의 실행원리 1편</a></strong> 에 포스팅 한 내용을 참조하면 더욱 좋다.</li>
</ul>
<br>

<h3 id="build-tool">Build Tool</h3>
<p>평소 Spring Boot 환경에서 자연스레 사용하던 gradle을 homebrew로 직접 설치하여 CLI 환경에서 실행해보는 실습을 진행하여 원리를 더욱 파악할 수 있게 되었다.</p>
<pre><code class="language-shell"># homebrew 이용 gradle 설치
brew install gradle

# 프로젝트 폴더로 이동하여 gradle 프로젝트 생성
# application -&gt; Java -&gt; Groovy -&gt; JUnit Jupyter
# JUnit Jupyter가 JUnit5를 의미하는 것
# 패키지는 com.programmers.java
gradle init

# 프로젝트 빌드
gradle build

# 프로젝트 실행
gradle run

# tasks 확인
gradle tasks</code></pre>
<ul>
<li>build script로 코틀린도 많이 사용하는 추세이지만, 아직까지는 익숙한 groovy로 진행하기로 했음</li>
<li>gradle이 수행되는 명령 단위를 <strong>task</strong>라고 한다.</li>
<li><code>build.gradle</code>에 task가 기록되어 있는데, <code>run</code>과 <code>build</code>같이 자주 사용되는 task는 Java application에서 사용할 것을 미리 만들어놔서 application 플러그인에 Java 프로그램을 실행시키기 위한 task가 들어가있다.</li>
</ul>
<br>

<h3 id="ide-intellij">IDE (IntelliJ)</h3>
<p>Build Tool을 사용하여 일일히 프로젝트를 생성하고 빌드하고 extension을 설치해서 task 확인하고 실행하는건 너무 불편하다는 사실을 모르고 당연하게 쓰던 IDE... 소중함을 알게 되었다.</p>
<br>

<h3 id="java-심화">Java 심화</h3>
<ol>
<li><p>Java는 기본형 타입인 boolean, byte, int, short, long, float, double, char를 제외한 모든 것이 <strong>참조 값(Reference value)</strong>이다.</p>
<ul>
<li>추가로, call by value와 call by reference에 대해서 언급해주셨는데, 기존에 알고 있던 바로는 Java에서 call by reference는 없다고 알고 있었다.</li>
<li>관련 내용은 <strong><a href="https://1-7171771.tistory.com/128">Call by value와 Call by reference</a></strong> 참고</li>
</ul>
</li>
<li><p>String Constant Pool에 관한 이야기</p>
<ul>
<li>Constant Pool, Runtime Constant Pool, String Pool에 관한 이야기는 사실 미묘한 차이가 약간씩 있다.</li>
<li><strong><a href="https://jae-yoon.tistory.com/17">Dev Uni 기술블로그 - Java의 실행원리 3편</a></strong> 에 포스팅 한 내용을 참조하면 더욱 좋다.</li>
</ul>
</li>
<li><p>String, StringBuilder, StringBuffer</p>
<ul>
<li>String : 문자열 연산 자체가 적고 멀티스레드의 경우  </li>
<li>StringBuffer : 문자열 연산이 많고 멀티스레드의 경우  </li>
<li>StringBuilder : 문자열 연산이 많고 단일스레드고 동기화를 고려하지 않아도 되는 경우  </li>
</ul>
</li>
</ol>
<blockquote>
<p>보통 Java-Spring에서는 멀티스레드 환경을 지원하고 있기 때문에 보통은 String, StringBuffer를 사용하는 편이다.</p>
</blockquote>
<ol start="4">
<li>Object 객체<ul>
<li><code>toString()</code></li>
<li><code>equals()</code></li>
<li><code>hashCode()</code></li>
<li>반드시 메서드 오버라이딩하여 사용하자. 자주 쓴다.</li>
</ul>
</li>
</ol>
<blockquote>
<p>동일성(Identity) vs 동등성(equality)</p>
<ul>
<li>동일성은 <code>==</code> 사용 시 참조하는 객체가 동일한지, 같은 객체를 가르키는지 확인하는 것</li>
<li>동등성은 객체가 같은 정보를 가지고 있는지 확인하는 것</li>
</ul>
</blockquote>
<blockquote>
<p><code>hashCode()</code>와 <code>eqauls()</code> 관계 : 찹쌀떡</p>
<ul>
<li>hash 값을 사용하는 컬렉션에서 논리적으로 같은지 비교</li>
<li>hash(true) → equals(false) <strong>다른 객체</strong></li>
<li>hash(true) → equals(true) <strong>동등 객체</strong></li>
</ul>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Web FrontEnd - Java Script]]></title>
            <link>https://velog.io/@jae_yoon/WebFrontEnd-k0llljoc</link>
            <guid>https://velog.io/@jae_yoon/WebFrontEnd-k0llljoc</guid>
            <pubDate>Tue, 03 Aug 2021 14:29:11 GMT</pubDate>
            <description><![CDATA[<h1 id="프론트엔드-java-script">프론트엔드 Java Script</h1>
<p>2021.09.12 ~
8월에 공부한 내용을 모두 잊어버려서 다시 정리中</p>
<h2 id="java-script-서론-링크">Java Script 서론, 링크</h2>
<p>Web Developer라면 필수적인 JavaScript를 공부하기로 마음먹었다. 프론트엔드를 희망하지만 node.js를 활용한 백엔드에도 관심이 생겨서 그 부분도 깊게 공부하려고 한다. 추후, 모던 자바스크립트 딥다이브 교재를 통하여 대비할 것이다.</p>
<hr>

<p>드림코딩 엘리 - JavaScript 기초강의 플레이리스트 
<a href="https://www.youtube.com/playlist?list=PLv2d7VI9OotTVOL4QmPfvJWPJvkmv6h-2">https://www.youtube.com/playlist?list=PLv2d7VI9OotTVOL4QmPfvJWPJvkmv6h-2</a></p>
<p>MDN JavaScript 문서
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript">https://developer.mozilla.org/ko/docs/Web/JavaScript</a></p>
<hr>
<br>

<h2 id="java-script1--역사현재미래">Java Script(1) : 역사/현재/미래</h2>
<blockquote>
<p>** 1. 동적인 웹사이트를 만들기 위한 노력 **</p>
</blockquote>
<p>마크 앤더슨은 원래 정적이었던 웹사이트 (_ HTML과 CSS로만 이루어져 있고 링크를 타고 페이지와 페이지 간에 이동 정도만 가능한 아주 정적인 웹사이트였다. _ ) <strong>Netscape Navigator Browser</strong>에 scripting 언어를 추가하여 동적인 웹사이트를 만들고자 하였다. 처음에 고려한 언어는 Java였지만, 웹 개발에는 좀 무겁기 때문에 브랜든이 만들었던 기존의 Scheme Scripting 언어를 수정해서 새로운 언어를 만들기로 했다. </p>
<p>시간이 없어서 빠르게 만들어야 했다. 따라서, 프로토타입을 베이스로 한 유연한 언어를 개발하게 되었다. 내부적으로 Mocha라고 불렸으며 나중에 <strong>LiveScript</strong>로 바뀌게 되었다. 그 당시에 Java의 인기가 많았기 때문에 그의 인기 덕을 보고 싶었던 Netscape 회사는 LiveScript를 <strong>JavaScript</strong>로 이름을 변경하였다. 따라서, <strong>Java와 JavaScript는 아무런 연관이 없는 언어이다.</strong></p>
<p>Netscape의 브라우저가 잘되자, Microsoft 역시 웹 사업에 뛰어들었다. Netscape사에서 출시한 브라우저를 reverse engineering 하게 되는데, 이것은 만들어진 프로그램의 바이너리 코드를 분석해서 소스 코드를 복원해내는 과정을 뜻한다. 한마디로 Netscape의 JavaScript를 고대<del>로 베껴와서 자신들이 만든 언어인 것처럼 한 것이다. <strong>Microsoft</strong>에서는 이 언어를 <strong>JScript</strong>라고 이름 짓게 된다. <strong>Internet Exploer</strong>에서는 이 JScript를 사용하게 되었다. ( ~</del>이것이 고통의 시.발.점~~ )</p>
<blockquote>
<p>** 2. ECMAScript 두두등장 **</p>
</blockquote>
<p>Microsoft사의 Internet Explorer는 JScript를 사용하고, Netscape사의 Netscape Navigator는 Javascript를 사용하다보니까, 둘은 달라서 다양한 브라우저에서 동작할 수 있도록 개발해야하는 개발자들은 너무 고통스러웠다. 예를 들어, 1996년 당시에 웹사이트를 인터넷 익스플로어로 열면 &quot;이 사이트는 Netscape Navigator로 더 잘보이니까 당장 다운받으세요!&quot;와 같은 문구가 지속적으로 나왔다. 반대도 마찬가지.</p>
<p>이 상황을 참지 못했던 Netscape사는 자신들이 Javascript를 만들었으니 그에 대한 표준안을 만들어보자고 ECMA International이라는 단체에 찾아가게 된다. 그리하여 탄생한 것이 <strong>ECMAScript 1</strong>이다.</p>
<p>근데, 망할 Microsoft가 거만해져서 자기들이 표준이라고 ECMAScript 표준안 이제는 안쓸거다 ! 이렇게 되면서 ECMAScript의 표준화 진행이 2000년도부터 매우 더디게 되었다.</p>
<blockquote>
<p>** 3. 춘추전국시대 **</p>
</blockquote>
<p>표준화를 앞에 두고 Firefox, Internet Exploer, Netscape Navigator 3사의 치열한 신경전이 벌어지고 있었다. 개발자들만 죽어나가고 있는 상황이었는데 2004년 제시 제임스가 제출한 종이에는 <strong>AJAX (Asynchronous JavaScript and XML)라는 비동기적으로 데이터를 서버에서 받아오고 처리할 수 있도록 도와주는 기술</strong>이 적혀있었다.</p>
<p>그러나, Ajax 기술이 도입되었음에도 표준화는 제대로 진행이 되지 않고 있는 상황이었다. 웹 브라우저는 점점 더 많아지는 상황에 개발자들은 웹 시장에 더욱 뛰어들게 되었다. 그러면서 개발자 간의 커뮤니티는 활성화 되었고 이런 커뮤니티 사에서 나오게 된 것이 바로 <strong>jQuery, dojo, mootools와 같은 라이브러리</strong>이다. 이 라이브러리들은 <strong>개발자가 각기 다른 브라우저에 대한 구현을 신경쓰지 않아도 되도록</strong> 하는 것을 원했다.</p>
<blockquote>
<p>** 4. 이 싸움을 끝내러왔다. **</p>
</blockquote>
<p>2008년 Google사의 <strong>JIT(just-in-time compilation)라는 강력한 엔진이 포함된 Chrome 브라우저가 등장했다.</strong> Javascript를 실행하는 속도가 매우 빠른 브라우저이다. 이에 자극을 받은 나머지 브라우저와 크롬은 서로 윈윈할 수 있는 구조를 만들기 위해 모여서 표준화를 진행하게 된다. 그렇게 2009년에 나온 것이 바로 <strong>ECMAScript 5</strong>가 나왔고, 2015년에 나온 <strong>ECMAScript 6</strong>이다. 매우 큰 변화는 저렇게 두 버전에서 많이 나왔다.</p>
<p>대부분의 웹 브라우저는 표준안 ECMAScript를 잘 따라가게 되었고, 더이상 jQuery와 같은 라이브러리는 필요로 하지 않게 되었다. </p>
<p>각각의 브라우저는 ECMAScript 표준안을 따라가는 엔진을 가지게 되었다. Chrome - V8 / Firefox - SpiderMonkey / Safari - JSCore / MS Edge - Chakra / Opera - Carakan / Adobe Flash - Tamarin 등등 다양한 엔진이 등장했다. 특히 V8 엔진은 node에서도 사용하는 엔진이다.</p>
<hr>

<p>[ 추가 (1) ]</p>
<p><strong>BABEL</strong>은 무엇일까?</p>
<p>시장에서 다양한 사용자들은 다양한 브라우저를 사용하고 있고, 또, 모든 사용자들이 최신 버전의 브라우저를 사용하는 것은 아니다. 하지만 개발자들은 최신 기능이 포함된 ECMAScript를 사용하고 싶어한다. 그래서 개발할 때는 최신버전의 ECMAScript를 사용하고 사용자에게 배포할 때만 <strong>JavaScript transcompiler</strong>를 이용해서 코드를 변환시켜서 배포하는데 이것을 가능하게 하는 것이 바로 <strong>BABEL</strong>이다.</p>
<hr>

<p>[ 추가 (2) ]</p>
<p><strong>SPA(Single Page Application)</strong>은 무엇일까?</p>
<p>최근 나아가고 있는 방향인데, 더이상 웹사이트를 만드는 것만으로는 충분하지가 않고 하나의 페이지 안에서 데이터를 받아와서 필요한 부분만 부분적으로 업데이트 하는 것이 굉장히 유행 중인데, JavaScript만으로도 구현이 가능 하겠지만, <strong>React, Vue, Angular</strong>등과 같은 라이브러리나 프레임워크를 통해서 SPA를 더 쉽게 구현하고자 한다.</p>
<p>또, Javascript를 활용한 방법은 무궁무진하다. node를 이용해서 백엔드 개발도 가능하고 리액트 네이티브 같은 것을 이용해서 모바일 어플리케이션도 일렉트론을 이용해서 데스크탑 어플리케이션도 만들 수 있다.</p>
<hr>

<h2 id="java-script2--html에-연결-방식">Java Script(2) : HTML에 연결 방식</h2>
<h3 id="1-head-내부">1. head 내부</h3>
<p><img src="https://images.velog.io/images/jae_yoon/post/b9115fa0-e397-4030-bc8d-42e9d8e856c4/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/jae_yoon/post/6eb781af-c8f8-4a0a-94a3-acb4d8cd85b1/image.png" alt=""></p>
<p>HTML을 parsing 하다가 </p>
<h3 id="2-body-끝">2. body 끝</h3>
<h3 id="3-head--async-속성">3. head + async 속성</h3>
<h3 id="4-head--defer-속성">4. head + defer 속성</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git / Github - Git ver.01]]></title>
            <link>https://velog.io/@jae_yoon/Git-Github</link>
            <guid>https://velog.io/@jae_yoon/Git-Github</guid>
            <pubDate>Fri, 30 Jul 2021 13:32:40 GMT</pubDate>
            <description><![CDATA[<h1 id="git-github-ver01">Git, Github ver.01</h1>
<hr>

<ul>
<li>Git의 기초를 정리한 게시글입니다.</li>
<li>최대한 구체적으로 작성해서 내용이 방대합니다.</li>
<li><a href="https://youtu.be/Z9dvM7qgN9s">https://youtu.be/Z9dvM7qgN9s</a> 강의를 토대로 내용을 정리했습니다.</li>
<li><a href="https://www.youtube.com/watch?v=8JJ101D3knE">https://www.youtube.com/watch?v=8JJ101D3knE</a> 강의를 토대로 내용을 정리했습니다.</li>
<li><a href="https://blog.naver.com/jdusans/222043705693">https://blog.naver.com/jdusans/222043705693</a> 도움을 많이 받은 게시글입니다.</li>
<li>branch, stash 등 심화내용(ver.02)은 유료 강의이므로 비공개 게시글로 작성했습니다.</li>
</ul>
<hr>
<br>

<h2 id="11-what-is-git--github-">1.1 What is Git / Github ?</h2>
<blockquote>
<p><strong>Git은 Version을 편리하게 관리하는 도구</strong></p>
</blockquote>
<p>Git은 버전을 편리하게 관리할 수 있도록 도와주는 도구이다. 우리가 작업하고 있는 파일들을 원하는 순간으로 다시 돌아갈 수 있게 해주는 마치 &#39;타임머신&#39;과 같은 역할을 한다. 명령어를 기반으로 한 <strong>CLI(Command-Line Interface)</strong> 프로그램이다.</p>
<p>CLI 환경에서 기본적인 명령어 / git 기본 명령어
<a href="https://velog.io/@grinding_hannah/Git-Git-%EC%82%AC%EC%9A%A9%EB%B2%95-%EB%B0%8F-%ED%84%B0%EB%AF%B8%EB%84%90-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%A0%95%EB%A6%AC">https://velog.io/@grinding_hannah/Git-Git-%EC%82%AC%EC%9A%A9%EB%B2%95-%EB%B0%8F-%ED%84%B0%EB%AF%B8%EB%84%90-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%A0%95%EB%A6%AC</a></p>
<p>CLI 환경에서 vi 에디터 편집을 위한 게시글 <a href="https://jhnyang.tistory.com/54">https://jhnyang.tistory.com/54</a></p>
<p>컴퓨터에 존재하는 대부분의 파일들은 버전 관리 시스템을 이용해서 관리할 수 있다. 이것을 VCS (Version Control System)라고 한다. VCS는 크게 두 가지로 나누어진다.</p>
<ul>
<li>CVS (Centralized Version Control)</li>
<li>DVCS (Distributed Version Control System)</li>
</ul>
<br>
버전 관리 시스템이 없었던 시절에는 폴더별로 이름을 version 1.0.0 , version 1.0.1과 같이 수동으로 일일이 관리하는 경우가 허다했다.

<p>이것을 개선하기 위하여 나온 것이 바로 <strong>CVS</strong>이다. CVS는 서버에 히스토리를 관리해서 각각의 개발자들이 원하는 내용을 서버에 업데이트 하면 즉각적으로 동기화가 이루어지는 시스템이다. 예시로는 CVS, SUBVERSION, PERFORCE가 있다.</p>
<p><strong>그러나</strong>, 서버에 문제가 생기거나 다운이 된다면 일을 진행하지 못하게 되고 오프라인에서 인터넷이 없는 경우 사용하지 못하는 치명적인 단점이 있다.</p>
<p>이 문제점을 개선한 것이 바로 <strong>DVCS</strong>이다. 서버에만 히스토리의 정보가 있는 것이 아니라,
<strong>모든 개발자들이 동일한 히스토리 정보를 가지고 있는 것</strong>을 의미한다. 서버에 문제가 생기거나 다운이 되어도 각각의 개발자들이 동일한 히스토리를 가지고 있기 때문에 서로의 정보를 이용하여 서버를 보관하고 계속해서 일을 진행할 수 있다. 인터넷이 없어도 오프라인에서 일을 진행할 수 있는 큰 장점이 있다.</p>
<p>DVCS는 회사에서만 이용할 수 있는 프라이빗 서버를 이용하는 경우도 있고 Github나 Bitbucket과 같은 클라우드를 이용하는 경우도 있다.</p>
<p><strong>Git</strong>은 delta-based가 아니라 <strong>프로젝트의 전체적인 내용을 스냅샷 해서 가지고 있다.</strong> 프로젝트 전체적인 내용을 가지고 있어서 version들 사이, branch들 사이를 오류 없이 굉장히 빠르게 이동할 수 있다. 또, *<em>변경 되지 않는 파일들은 예전의 파일들의 링크를 가리키고 있기 때문에 각각의 스냅샷은 무겁지가 않고 굉장히 가볍다. *</em>
<br></p>
<h2 id="12-install-settings">1.2 Install, Settings</h2>
<blockquote>
<p><strong>Install WSL2 ( Windows Subsystem for Linux 2 ) #for terminal</strong></p>
</blockquote>
<p>Linux, Mac 환경에서는 터미널을 이용하면 되지만, 나는 Windows 환경이기 때문에 따로 터미널을 설치할 필요가 있었다. Git Bash를 이용해도 되지만, 윈도우 환경에서 리눅스를 사용할 수 있게 도와주는 WSL2를 설치해서 사용하기로 했다.</p>
<ul>
<li><p>WSL2 설치 및 사용 방법 <a href="https://www.44bits.io/ko/post/wsl2-install-and-basic-usage">https://www.44bits.io/ko/post/wsl2-install-and-basic-usage</a></p>
</li>
<li><p>터미널 커스터마이징 하기 <a href="https://nomadcoders.co/windows-setup-for-developers/lobby">https://nomadcoders.co/windows-setup-for-developers/lobby</a></p>
</li>
</ul>
<p>WSL2를 설치하고 나서, 기본 환경을 Windows PowerShell이 아닌 Ubuntu로 변경하기 위하여 터미널 환경에서 Ctrl + ,를 눌러서 json 파일을 열고 설정을 해줬다. &quot;defaultProfile&quot;에서 PowerShell의 고유값이 기본값으로 설정되어있는데 이것을 Ubuntu의 고유값으로 바꿔주면 된다.</p>
<p>이전에 나는 우분투를 사용해본적이 있어서 시작 디렉토리가 어색했기에 Ubuntu로 설정값을 바꿨다. 이 명령어를 파일에 추가하면 된다.</p>
<pre><code>&quot;startingDirectory&quot;: &quot;\\\\wsl$\\Ubuntu-20.04\\home\\jae_yun&quot;</code></pre><p>WSL2를 활용한 Linux 환경에서는 루트 디렉토리의 mnt를 이용해서 윈도우 세계로 들어갈 수 있다.</p>
<p><img src="https://images.velog.io/images/jae_yoon/post/114070e6-82ee-4f3f-81f1-e906197699ac/image.png" alt=""></p>
<p>윈도우 파일 탐색기에서 폴더를 가시적으로 보고싶다면 폴더 검색창에 <strong>\\wsl$</strong> 를 입력하면 볼 수 있다. Linux의 세계에서 Windows 파일을 건드는 것은 크게 문제가 되지 않지만, <del>Windows 세계에서 Linux 파일은 웬만하면 절대 건들지말자.</del></p>
<p><img src="https://images.velog.io/images/jae_yoon/post/aa641b09-3ed4-413e-939e-42a54cc87d28/image.png" alt=""></p>
<br>

<blockquote>
<p><strong>Setting Git</strong></p>
</blockquote>
<p><a href="https://git-scm.com/book/ko/v2/Git%EB%A7%9E%EC%B6%A4-Git-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0">https://git-scm.com/book/ko/v2/Git%EB%A7%9E%EC%B6%A4-Git-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</a></p>
<p>새로운 환경에서 Git을 사용할 때 처음에 설정하면 편한 것들을 정리하겠다.</p>
<p><code>git config --global user.name &quot;이름&quot;</code>
<code>git config --global user.email &quot;이메일&quot;</code>
<code>git config --global core.autocrlf true</code> Windows
<code>git config --global core.autocrlf input</code> Linux/Mac</p>
<p>Windows 운영체제라면 core.autocrlf true를 Linux나 Mac이라면 core.autocrlf input을 사용한다.</p>
<p>이는 운영체제에서 줄 바꿈을 할 때 문제를 야기하기 때문이다. 줄 바꿈 문자에는 \r라는 CRLF (Carriage-Return)과 \n라는 LF (Line Feed) 이렇게 두 가지가 있는데, Windows 에서는 CRLF와 LF 문자를 둘 다 사용하지만 Linux/Mac에서는 LF 문자만 사용하기 때문이다.</p>
<p>Git repository는 다양한 유저들이 다양한 운영체제로 사용하기 때문에 문제점이 발생할 수 있다. Git은 커밋할 때 자동으로 CRLF를 LF로 변환해주고 반대로 Checkout 할 때 LF를 CRLF로 변환해 주는 기능이 있는데 이것이 바로 <code>core.autocrlf</code>이다. 따라서, Windows 에서는 이 기능을 <strong>true</strong>로 해서 사용해야한다.</p>
<p>Linux/Mac에서는 CRLF를 사용하지 않기 때문에 Checkout 과정에서 LF를 CRLF로 변환해 줄 필요가 없다. 따라서 <code>core.autocrlf</code>값을 <strong>input</strong>으로 설정하고 커밋할 때만 CRLF를 LF로 변환하면 되는 것이다.</p>
<hr> 

<p><strong>※주의</strong>
내 운영체제는 Windows 이지만, WSL2를 이용하여 Linux 환경을 이용하여 git을 사용해서 그런지 true로 하면 오류가 생겼고 input으로 해야 괜찮았다. 현재는 input으로 설정해서 사용 중이다.</p>
<hr>

<p>텍스트 에디터를 이용하여 보기 쉽게 편집하려면 <code>git config --global -e</code> 를 이용하면 실행된다. 이때, 터미널 환경에서 에디터를 이용하여 편집하고 그를 저장하고 종료를 해야 다시 터미널을 사용할 수 있도록 하려고 설정하려면 <code>git config --global core.editor &quot;code --wait&quot;</code> 를 입력하면 된다.</p>
<p><img src="https://images.velog.io/images/jae_yoon/post/b52e35d9-91d2-4d55-8637-b97c6c3f7b11/image.png" alt=""></p>
<p><code>code .</code>을 입력하면 에디터가 빠르게 열린다. push와 pull은 더 편리하게 사용하려고 따로 추가한 설정값이다. 
<br></p>
<h2 id="21-git-workflow">2.1 Git Workflow</h2>
<blockquote>
<p>*<em>Git은 크게 3가지 작업환경을 가진다. *</em></p>
</blockquote>
<hr>

<ol>
<li><p>working tree</p>
</li>
<li><p>staging area</p>
</li>
<li><p>git repository</p>
</li>
</ol>
<hr>

<p>working tree는 프로젝트의 파일들을 작업하고 있는 디렉토리를 말한다.</p>
<p>staging area는 버전 히스토리에 저장할 준비가 되어있는 파일들을 옮겨놓는 디렉토리를 말한다.</p>
<p>git repository는 버전 히스토리를 가지고 있는 디렉토리를 말한다. 한마디로 저장소이다.</p>
<blockquote>
<p>working tree에서 a, b, c라는 파일들을 작업하고 있는 상황이다.</p>
</blockquote>
<p>b, c파일은 저장할 준비가 됐다고 하면, working tree에서 staging area로 옮긴다. 다음으로 staging area에서 git repository로 옮길 때는 commit 명령어를 사용해서 저장한다. 나중에 a 파일도 저장할 준비가 되면 동일한 작업을 한다. git repository에 저장된 버전들은 checkout 명령어를 이용해서 언제든지 원하는 버전으로 다시 돌아갈 수 있다.</p>
<p>저장된 Git 히스토리는 나의 로컬 컴퓨터에만 보관되기 때문에 히스토리를 전부 날릴 수도 있다. 따라서, push 명령어를 이용해 Github와 같은 서버에 나의 git directory를 업로드 하는 것이다. 서버에서 로컬로 다시 다운받으려면 pull 명령어를 사용한다.</p>
<p><code>add</code> : working tree -&gt; staging area</p>
<p><code>commit</code> : staging area -&gt; git repository</p>
<p><code>push</code> : git repository -&gt; Github</p>
<p><code>pull</code> : Github -&gt; git repository</p>
<p><img src="https://images.velog.io/images/jae_yoon/post/427681c5-7c1a-46ae-85a6-51ea052aaf49/image.png" alt=""></p>
<p>working tree는 세부적으로 다음과 같이 나뉜다.</p>
<hr>

<ul>
<li><p>untracked</p>
</li>
<li><p>tracked</p>
<ul>
<li>unmodified</li>
<li>modified</li>
</ul>
</li>
</ul>
<hr>

<p><strong>untracked</strong> : 새로 만들어진 파일이거나 기존에 존재하던 프로젝트에서 git을 초기화 한 파일. 그렇게 되면 git은 이 파일에 대한 정보가 전혀 없어서 트래킹 하지 못하는 상태이다.</p>
<p><strong>tracked</strong> : git이 이미 알고 있는, git이 이미 트래킹하고 있는 것을 뜻한다.</p>
<p>git이 트래킹 하고 있는 tracked 파일들 중에서도 지금 이 시점에서 수정이 되었는지 유무에 따라 unmodified / modified로 나누어진다.</p>
<p><strong>unmodified</strong> : 이전 버전에 대해서 수정되지 않은 상태</p>
<p><strong>modified</strong> : 이전 버전에 대해서 수정된 상태</p>
<p>따라서, 수정이 된 modified 파일만 working tree에서 staging area로 옮길 수 있다.</p>
<p>각각의 버전, commit에는 스냅샷 된 정보들을 기반으로 해서 고유한 해쉬코드가 부과가 된다. 이것을 통해서 버전 정보를 참조할 수 있는데, commit에는 아이디 말고도 어떤 버전인지 버전과 관련된 메시지, 누가 작성했는지 날짜와 시간 같은 정보들도 함께 포함되어져 있다.</p>
<h2 id="22-git-add---add-local-files">2.2 Git add - add Local files</h2>
<p>새로 파일을 생성하고 상태를 보기 위하여 <code>git status</code>를 하면 untracked file임을 확인할 수 있다. 따라서 working tree의 파일을 staging area로 올리기 위해 사용하는 것이 바로 <code>git add</code> 명령어다.</p>
<p>파일을 수정하고 다시 상태를 보면 기존에 add한 파일은 staging area에 있어서 commit을 할 준비가 되어있는 상태인데 추가로 working tree에 modified 파일이 생기는 것을 알 수 있다. 이때 다시 git add 하면 modified 파일이 staging area에 가면서 파일에 버전 히스토리가 생성되는 것을 확인할 수 있다.</p>
<p><img src="https://images.velog.io/images/jae_yoon/post/35554ee6-89aa-469a-9007-a0c92b6d5ca2/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/jae_yoon/post/9e9e76b6-a63f-453e-a25b-9efd149a7d4d/image.png" alt=""></p>
<p>SourceTree를 활용하여 GUI 환경에서 쉽게 볼 수도 있다.</p>
<p><img src="https://images.velog.io/images/jae_yoon/post/4b8b279f-a484-47e1-b62b-eba0088c728d/image.png" alt=""></p>
<p><code>git rm --cached &lt;file&gt;</code>명령어를 입력하면 다시 staging area에서 working tree로 이동 가능하다. --cached는 --staged와 거의 동일한 표현이라고 알면 된다.</p>
<p>&quot; 어라, rm 명령어는 CLI 환경에서 파일을 삭제하는 명령어가 아니었나? &quot;</p>
<p>맞다. staging area에서 삭제한다는 의미인데, 삭제하고 working tree로 내리는 것이다. </p>
<hr>

<p><strong>파일 변경 시 유용한 TIP</strong></p>
<p>working tree에서 파일을 삭제(rm)하거나 이동(mv)하면 working tree 에서만 유효하기 때문에 작업하고 add를 해서 다시 staging area에 적용하고 commit 해야한다.</p>
<p>그러나, git에서 제공하는 git rm / git mv를 쓰면 staging area에 바로 적용되는 것을 확인할 수 있다.</p>
<ul>
<li><p>staging area -&gt; working tree 할 때 <code>git rm --cached &lt;file&gt;</code>을 쓴다.</p>
</li>
<li><p>working tree에서 작업한 것을 바로 staging area에 적용하고 싶을 때 <code>git rm</code> / <code>git mv</code>를 쓴다.</p>
</li>
</ul>
<hr>
<br>

<p><img src="https://images.velog.io/images/jae_yoon/post/1a447e92-565f-4438-b149-6cdfc6e55d7e/image.png" alt=""></p>
<p>다음으로, 만약 a.txt 파일을 삭제했다고 하자. working tree에 a.txt 파일이 없었기 때문에 staging area에 추가되지 않는 것을 확인 가능하다. 이때 <code>git add .</code> 명령어를 사용하면 모든 것을 포함해서 staging area에 추가하는 것을 확인할 수 있다.</p>
<p><img src="https://images.velog.io/images/jae_yoon/post/14f7c794-4a85-47c8-8f15-f8d604656105/image.png" alt="">
<br></p>
<h2 id="23-git-ignore">2.3 Git ignore</h2>
<p>Git과 Github에 올리고 싶지 않은, 트래킹 하고 싶지 않은 파일들은 git ignore 파일에 추가해서 트래킹을 피할 수 있다.</p>
<p>.gitignore 파일을 만들어서 내용을 수정하면 된다.</p>
<ul>
<li>특정한 파일 이름</li>
<li>특정한 형식 모두</li>
<li>특정 디렉토리</li>
<li>특정 디렉토리 안의 파일</li>
</ul>
<p>예를 들어서, .log 파일을 트래킹 하고싶지 않다고 해보자.</p>
<p><img src="https://images.velog.io/images/jae_yoon/post/67789f8c-5f58-4244-8dcb-1c6ebea56060/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/jae_yoon/post/96e778a0-0a80-4424-a73b-ba0a0e1303ac/image.png" alt=""></p>
<p>위의 네 가지 형식을 모두 나타낸 것이다.</p>
<p><img src="https://images.velog.io/images/jae_yoon/post/2f2acb90-cf62-4363-9b23-42d328b0c416/image.png" alt=""></p>
<p>.gitignore 형태로 트래킹되지 않는 것을 확인할 수 있다.
<br></p>
<h2 id="24-git-status--diff">2.4 Git status / diff</h2>
<p>git status를 이용해서 상태를 확인할 때 <code>git status -s</code>를 이용해서 간단한 형태로 확인 가능하다.</p>
<p><img src="https://images.velog.io/images/jae_yoon/post/a5af514e-945d-45f1-b518-a78d8546ad66/image.png" alt=""></p>
<p>git status를 이용하면 내용이 변경된 것은 확인 가능하지만 <strong>어떤 내용이 변경되었는지</strong>는 확인이 불가능하다. 이때 <code>git diff</code>를 이용하여 이전 파일과 변경된 파일 간의 차이를 알아낼 수 있다.</p>
<p>git diff에서 기본적으로 -속성은 이전 파일이고 +속성은 변경된 파일을 뜻한다. git diff를 이용하면 working tree의 변경사항만 확인 가능하고 git diff --staged / git diff --cached를 이용하면 staging area의 변경사항을 확인할 수 있다.</p>
<p><img src="https://images.velog.io/images/jae_yoon/post/866792a7-d19b-4215-95e2-749ad8ff1abb/image.png" alt=""></p>
<p>-1은 이전 파일의 첫 번째 줄을 확인하라는 것이고 +1,2는 현재 파일의 첫 번째 줄에서 두 번째 줄 까지 확인하라는 의미이다.</p>
<p><img src="https://images.velog.io/images/jae_yoon/post/9662d4a7-cd32-46fa-a50e-8db839e29228/image.png" alt=""></p>
<blockquote>
<p>diff를 보기 쉽게 에디터로 보고싶어요 !!</p>
</blockquote>
<p><code>git config --global -e</code>를 이용해서 에디터를 연 다음에 다음을 추가하자.</p>
<pre><code>[diff]
    tool = vscode
[difftool &quot;vscode&quot;]
    cmd = code --wait --diff $LOCAL $REMOTE</code></pre><p>그러고 나서 <code>git difftool</code> 혹은 <code>git difftool --staged</code> 를 이용하면 된다.
<br></p>
<h2 id="25-git-commit">2.5 Git commit</h2>
<blockquote>
<p>*<em>드디어 version을 만들 준비가 되었어요! *</em></p>
</blockquote>
<p>staging area의 파일들을 git repository로 이동시키기 위하여 <code>git commit</code>을 하면 에디터가 열린다. 에디터에서 Title(제목)과 Description(상세 내용)을 입력하면 된다.</p>
<p><img src="https://images.velog.io/images/jae_yoon/post/54259e7f-eb3b-4ec3-a093-c8f7fc78540c/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/jae_yoon/post/a4875ac5-2269-4a56-ae74-e8eceebb95f2/image.png" alt=""></p>
<p><code>git log</code>를 확인하면 세부적인 정보 확인이 가능하다.</p>
<p><img src="https://images.velog.io/images/jae_yoon/post/04316a7c-0d28-401f-ace3-df15a482d9d7/image.png" alt=""></p>
<p>전체적인 해시코드, 작성자, 날짜, 내용을 볼 수 있다.</p>
<p>위와 같이 git commit을 이용하면 Title과 Description을 작성해야 하기 때문에 commit에 옵션을 줘서 간단하게 커밋하는 경우가 많다.</p>
<p><code>git commit -m &quot;&quot;</code>을 이용해서 커밋을 바로 해도 된다. 혹은 working tree의 파일이 너무 마음에 들어서 staging area로 올리지 않고 바로 repository로 올려버리고 싶으면 <code>git commit -a &quot;&quot;</code>으로 해도된다. 심지어 메세지까지 마음에 든다면 a(all) 옵션과 묶어서 m(message) 옵션을 같이 사용해서 <code>git commit -am &quot;&quot;</code>고 과 같이 사용하면 된다.</p>
<p><img src="https://images.velog.io/images/jae_yoon/post/9e18eba8-2e24-41af-9dab-c7b797784e11/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/jae_yoon/post/616e745c-d122-4dff-82bb-0df43f5d7949/image.png" alt=""></p>
<blockquote>
<p><strong>commit 할 때 Tip !!</strong></p>
</blockquote>
<p>git repository에 있는 commit 들은 <strong>history의 창고이다.</strong></p>
<p>어플리케이션을 만드는 작업을 한다고 가정하자. 히스토리에 전체적인 어플리케이션을 만들어서 하나의 commit으로 저장하면 아무런 의미가 없다. 예를 들어, 기능별로 작은 단위로 만들어 나가는 것이 중요하다.</p>
<p>또, 의미 있는 이름으로 commit해서 저장해야 작업내용을 빠르게 확인 가능하고 내가 원하는 변경사항만 빠르게 찾아서 자세하게 확인 가능하고 원하지 않는 commit을 취소 할 때도 편하다.</p>
<ul>
<li><p>큰 단위로 한꺼번에 히스토리에 저장하기 보다는 작은 단위로 나누어서 히스토리에 저장하자.</p>
</li>
<li><p>히스토리에 의미가 없는 이름으로 commit 하지 말고 의미 있는 이름으로 지정해서 저장하자.</p>
</li>
<li><p>commit의 메시지는 보통 현재형 그리고 동사로 만든다. init, add, fix 이런 식으로</p>
</li>
</ul>
<blockquote>
<p><strong>commit 할 때 주의점 !!</strong></p>
</blockquote>
<p>내가 크래쉬를 고쳤다고 하면 정말로 그 고친 내용만 포함하는 commit을 만들어야지 이왕 하는 김에 다른 버그도 하나 고치고~ 리팩토링도 하고~ 새로운 기능도 넣어보고~ 이런식으로 commit 절대로 ❌❌ 코드 리뷰할 때 혹은 히스토리를 바라볼 때 너무너무 혼동이 옵니다.</p>
<ul>
<li>commit 메시지에 맞게 해당하는 내용만 포함해서 commit 하자 !! 제발 !!</li>
</ul>
<br>

<h1 id="summary">Summary</h1>
<p><a href="https://www.youtube.com/watch?v=8JJ101D3knE">https://www.youtube.com/watch?v=8JJ101D3knE</a> 유튜버 Mosh의 git 강의에서 &quot;FREE Git cheat sheet&quot;를 참고로 정리했습니다. <a href="https://bit.ly/33rnYLt">https://bit.ly/33rnYLt</a></p>
<ul>
<li><p>About WSL2 settings</p>
<ul>
<li>&quot;startingDirectory&quot;: &quot;\\wsl$\Ubuntu-20.04\home\jae_yun&quot;</li>
<li>\\wsl$<br>
</li>
</ul>
</li>
<li><p>About Git settings</p>
<ul>
<li>git config --global user.name &quot;이름&quot;</li>
<li>git config --global user.email &quot;이메일&quot;</li>
<li>git config --global core.autocrlf true (Windows)</li>
<li>git config --global core.autocrlf input (Linux/Mac)<br>
</li>
</ul>
</li>
<li><p>Edit using Editor</p>
<ul>
<li>git config --global -e</li>
<li>git config --global core.editor &quot;code --wait&quot;</li>
<li>code .<br>
</li>
</ul>
</li>
<li><p>Initializing a repository</p>
<ul>
<li>git init &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# initialize git</li>
<li>rm -rf .git &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# delete .git<br>
</li>
</ul>
</li>
<li><p>Staging files</p>
<ul>
<li>git add file &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# stages a single file</li>
<li>git add file1 file2 &nbsp;&nbsp;# stages multiple files</li>
<li>git add * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# stage all files except untracked files ( delete file / new file)</li>
<li>git add . &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# stage all<br>
</li>
</ul>
</li>
<li><p>Viewing the status</p>
<ul>
<li>git status &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Full status</li>
<li>git status -s &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Short status<br>
</li>
</ul>
</li>
<li><p>Commiting the staged files</p>
<ul>
<li>git commit &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# opens the default editor</li>
<li>git commit -m &quot; &quot; &nbsp;# commits with a one-line message</li>
<li>git commit -am &quot; &quot; # skipping the staging area<br>
</li>
</ul>
</li>
<li><p>Removing / Moving files</p>
<ul>
<li>git rm file 
&nbsp; # Removes from working tree and staging area</li>
<li>git rm --cached file / git rm --staged file
&nbsp;# Removes from staging area only</li>
<li>git mv file1 file2<br>
</li>
</ul>
</li>
<li><p>Ignoring files ( 
You can ignore it by writing the following in &quot;.gitignore.txt&quot;:)</p>
<ul>
<li>특정한 파일 이름</li>
<li>특정한 형식 모두</li>
<li>특정 디렉토리</li>
<li>특정 디렉토리 안의 파일<br>
</li>
</ul>
</li>
<li><p>Viewing the staged / Unstaged changes</p>
<ul>
<li>git diff &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Shows unstaged(working tree) changes</li>
<li>git diff --cached / git diff -staged
&nbsp;# Shows staged changes</li>
</ul>
</li>
</ul>
<p>Add this code to <code>git config --global -e</code>, if you want to use the editor.</p>
<pre><code>[diff]
    tool = vscode
[difftool &quot;vscode&quot;]
    cmd = code --wait --diff $LOCAL $REMOTE</code></pre><p>You can use <code>git difftool</code> / <code>git difftool --staged</code>
<br></p>
<ul>
<li>Viewing the history<ul>
<li>git log &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Full history</li>
<li>git log --oneline &nbsp;&nbsp;# Summary</li>
<li>git log --reverse
&nbsp;# Lists the commits from the oldest to the newest</li>
</ul>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>