<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>taehee-kim-dev.log</title>
        <link>https://velog.io/</link>
        <description>Web Back-End (Spring, JPA, AWS)</description>
        <lastBuildDate>Wed, 18 Aug 2021 11:01:59 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>taehee-kim-dev.log</title>
            <url>https://images.velog.io/images/taehee-kim-dev/profile/1db12acd-8c37-4ce1-8252-395a866e3a9d/BE_인비(김태희)_프로필사진.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. taehee-kim-dev.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/taehee-kim-dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[우아한테크코스 3기 백엔드 Lv4 모의면접 대상 Lv3 학습로그 모음]]></title>
            <link>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv4-%EB%AA%A8%EC%9D%98%EB%A9%B4%EC%A0%91-%EB%8C%80%EC%83%81-Lv3-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8-%EB%AA%A8%EC%9D%8C</link>
            <guid>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv4-%EB%AA%A8%EC%9D%98%EB%A9%B4%EC%A0%91-%EB%8C%80%EC%83%81-Lv3-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8-%EB%AA%A8%EC%9D%8C</guid>
            <pubDate>Wed, 18 Aug 2021 11:01:59 GMT</pubDate>
            <description><![CDATA[<h2 id="querydsl-동적-쿼리-작성">[QueryDSL] 동적 쿼리 작성</h2>
<h3 id="내용">내용</h3>
<ul>
<li>QueryDSL을 사용해 동적인 조건에 따라 SQL 쿼리문을 생성하는 동적 쿼리를 작성했다.</li>
<li>하나의 쿼리문으로 여러 조건의 쿼리를 생성할 수 있었다.</li>
<li>객체의 함수를 참조하는 QueryDSL의 사용 방식이 가독성, 생산성, 유지보수성을 향상시켰다.<h3 id="링크">링크</h3>
</li>
<li><a href="https://github.com/woowacourse-teams/2021-cvi/blob/develop/backend/src/main/java/com/backjoongwon/cvi/post/domain/PostRepositoryImpl.java">https://github.com/woowacourse-teams/2021-cvi/blob/develop/backend/src/main/java/com/backjoongwon/cvi/post/domain/PostRepositoryImpl.java</a><h3 id="태그">태그</h3>
SQL, QueryDSL</li>
</ul>
<br>

<h2 id="log-logging">[Log] Logging</h2>
<h3 id="내용-1">내용</h3>
<ul>
<li>LogBack의 Log Appender를 사용해 애플리케이션의 로그를 남겼다.</li>
<li>로그 출력 조건 및 형식 설정과 파일 분리, 백업 조건을 세밀하게 설정할 수 있었다.</li>
<li>AWS CloudWatch 대시보드에 연동해, 터미널을 통한 EC2 인스턴스 접속 없이 대시보드에서 로그 파일들의 내용을 확인할 수 있게 했다.<h3 id="링크-1">링크</h3>
</li>
<li><a href="https://github.com/woowacourse-teams/2021-cvi/tree/develop/backend/src/main/resources">https://github.com/woowacourse-teams/2021-cvi/tree/develop/backend/src/main/resources</a></li>
<li><a href="https://ap-northeast-2.console.aws.amazon.com/cloudwatch/home?region=ap-northeast-2#dashboards:name=DASHBOARD-cvi-korea">https://ap-northeast-2.console.aws.amazon.com/cloudwatch/home?region=ap-northeast-2#dashboards:name=DASHBOARD-cvi-korea</a><h3 id="태그-1">태그</h3>
Log, LogBack</li>
</ul>
<br>

<h2 id="git-submodule">[Git] Submodule</h2>
<h3 id="내용-2">내용</h3>
<ul>
<li>DB 계정, JWT secret key 등 보안이 필요한 파일들을 git submodule로 관리했다.</li>
<li>로컬, 테스트 서버, 서비스 서버 각각에 적용되는 보안 파일들의 관리 포인트가 많았다.</li>
<li>Submodule로 관리 포인트를 줄일 수 있었다.<h3 id="링크-2">링크</h3>
</li>
<li><a href="https://github.com/woowacourse-teams/2021-cvi/blob/develop/.gitmodules">https://github.com/woowacourse-teams/2021-cvi/blob/develop/.gitmodules</a><h3 id="태그-2">태그</h3>
Git, 보안</li>
</ul>
<br>

<h2 id="배포-무중단-배포">[배포] 무중단 배포</h2>
<h3 id="내용-3">내용</h3>
<ul>
<li>Jenkins와 shell script로 Blue-green 배포 방식을 적용해 서비스 서버와 테스트 서버를 무중단으로 배포했다.</li>
<li>서비스 사용자들은 중단 없이 업데이트된 서비스를, 프론트엔드 크루들은 중단 없이 업데이트된 테스트 서버의 API를 사용할 수 있었다.<h3 id="링크-3">링크</h3>
</li>
<li><a href="https://github.com/woowacourse-teams/2021-cvi/tree/develop/backend/deploy">https://github.com/woowacourse-teams/2021-cvi/tree/develop/backend/deploy</a><h3 id="태그-3">태그</h3>
배포</li>
</ul>
<br>

<h2 id="jpa-일급-컬렉션-적용">[JPA] 일급 컬렉션 적용</h2>
<h3 id="내용-4">내용</h3>
<ul>
<li>JPA Entity에 Entity에 관한 비즈니스 로직 함수가 너무 많아졌다.</li>
<li>Entity 컬렉션을 JPA의 @Embedded, @Embeddable 어노테이션을 사용해 일급 컬렉션화 시켰다.</li>
<li>관련된 비즈니스 로직들을 일급 컬렉션 내부로 숨겨 캡슐화 시킬 수 있었다.<h3 id="링크-4">링크</h3>
</li>
<li><a href="https://github.com/woowacourse-teams/2021-cvi/blob/develop/backend/src/main/java/com/backjoongwon/cvi/post/domain/Comments.java">https://github.com/woowacourse-teams/2021-cvi/blob/develop/backend/src/main/java/com/backjoongwon/cvi/post/domain/Comments.java</a><h3 id="태그-4">태그</h3>
JPA, OOP</li>
</ul>
<br>

<h2 id="db-마이그레이션">[DB] 마이그레이션</h2>
<h3 id="내용-5">내용</h3>
<ul>
<li>로컬 개발 환경에서 이루어진 DB 업데이트를 테스트 서버, 서비스 서버에 모두 반영할 때 관리포인트가 많아 실수의 여지가 있었다.</li>
<li>DB 마이그레이션 툴 Flyway를 사용해 관리 포인트를 줄였다.<h3 id="링크-5">링크</h3>
</li>
<li><a href="https://github.com/woowacourse-teams/2021-cvi/tree/develop/backend/src/main/resources/db/migration">https://github.com/woowacourse-teams/2021-cvi/tree/develop/backend/src/main/resources/db/migration</a><h3 id="태그-5">태그</h3>
DB</li>
</ul>
<br>

<h2 id="cache-ehcache-scheduling-적용">[Cache] Ehcache, Scheduling 적용</h2>
<h3 id="내용-6">내용</h3>
<ul>
<li>국가 공공 데이터 포털 API로부터 전국 백신 접종 관련 대용량 데이터를 받아왔다.</li>
<li>데이터 양이 많아 API 호출 비용이 컸고, 이 데이터는 매일 아침 10시마다 업데이트 됐다. 또한 클라이언트 쪽에서는 이 데이터를 단순히 읽기만 했다.</li>
<li>Ehcache를 사용해 해당 데이터를 캐싱했다. 아침 10시마다 데이터가 업데이트되므로 Spring의 Scheduling을 사용해 캐싱 데이터를 업데이트했다. 클라이언트 측에서 API 요청을 했을 때의 지연 시간을 934ms 에서 24.44ms로 40배 이상 줄였다.<h3 id="링크-6">링크</h3>
</li>
<li><a href="https://github.com/woowacourse-teams/2021-cvi/blob/develop/backend/src/main/resources/ehcache.xml">https://github.com/woowacourse-teams/2021-cvi/blob/develop/backend/src/main/resources/ehcache.xml</a></li>
<li><a href="https://github.com/woowacourse-teams/2021-cvi/tree/develop/backend/cache/data">https://github.com/woowacourse-teams/2021-cvi/tree/develop/backend/cache/data</a></li>
<li><a href="https://github.com/woowacourse-teams/2021-cvi/blob/develop/backend/src/main/java/com/backjoongwon/cvi/publicdata/service/PublicDataService.java">https://github.com/woowacourse-teams/2021-cvi/blob/develop/backend/src/main/java/com/backjoongwon/cvi/publicdata/service/PublicDataService.java</a></li>
</ul>
<h3 id="태그-6">태그</h3>
<p>Cache</p>
<br>

<h2 id="sql-쿼리-최적화">[SQL] 쿼리 최적화</h2>
<h3 id="내용-7">내용</h3>
<ul>
<li>쿼리 최적화를 위해 fetch join, default_batch_fetch_size 설정을 사용했다.</li>
<li>Lazy loading으로 발생할 수 있는 N+1 select 등 여러 개의 쿼리 발생 문제를 fetch join으로 최적화했다.</li>
<li>JPA 엔티티의 필드에 두 개 이상의 엔티티 컬렉션이 있을 경우 default_batch_fetch_size 설정으로 쿼리를 최적화했다.<h3 id="링크-7">링크</h3>
</li>
<li><a href="https://github.com/woowacourse-teams/2021-cvi/blob/develop/backend/src/main/java/com/backjoongwon/cvi/post/domain/PostRepositoryImpl.java">https://github.com/woowacourse-teams/2021-cvi/blob/develop/backend/src/main/java/com/backjoongwon/cvi/post/domain/PostRepositoryImpl.java</a></li>
<li><a href="https://github.com/woowacourse-teams/2021-cvi/blob/develop/backend/src/main/resources/application.yml">https://github.com/woowacourse-teams/2021-cvi/blob/develop/backend/src/main/resources/application.yml</a><h3 id="태그-7">태그</h3>
SQL, JPA, QueryDSL</li>
</ul>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[[OAuth]Spring Security 없이 카카오 로그인 구현하기]]></title>
            <link>https://velog.io/@taehee-kim-dev/Spring-Security-%EC%97%86%EC%9D%B4-%EC%B9%B4%EC%B9%B4%EC%98%A4-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@taehee-kim-dev/Spring-Security-%EC%97%86%EC%9D%B4-%EC%B9%B4%EC%B9%B4%EC%98%A4-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 06 Jul 2021 04:22:26 GMT</pubDate>
            <description><![CDATA[<h2 id="oauthopen-authorization-개념">OAuth(Open Authorization) 개념</h2>
<p>산업 표준 프로토콜이다.</p>
<h3 id="왜-사용하는가">왜 사용하는가?</h3>
<ul>
<li>사용자는 새로운 서비스에 회원가입(ID/PW 제공)하기 꺼려한다.</li>
<li>기존에 가입되어있는 서비스(Kakao)에 로그인(인증)하고 정보를 선택적으로 제공(인가)하여 새로운 서비스에 로그인 할 수 있다.</li>
</ul>
<h3 id="openid와의-차이">OpenID와의 차이</h3>
<p>OpenID도 인증을 위한 표준 프로토콜이고 HTTP를 사용한다는 점에서는 OAuth와 같다. </p>
<p>그러나 OpenID와 OAuth의 목적은 다르다. OpenID의 주요 목적은 인증(Authentication)이지만, OAuth의 주요 목적은 허가(Authorization)이다.</p>
<p>물론 OAuth에서도 인증 과정이 있다. 가령 Facebook의 OAuth를 이용한다면 Facebook의 사용자인지 인증하는 절차를 Facebook(Service Provider) 처리한다. 하지만 OAuth의 근본 목적은 해당 사용자의 담벼락(wall)에 글을 쓸 수 있는 API를 호출할 수 있는 권한이나, 친구 목록을 가져오는 API를 호출할 수 있는 권한이 있는 사용자인지 확인하는 것이다.</p>
<p>OAuth를 사용자 인증을 위한 방법으로 쓸 수 있지만, OpenID와 OAuth의 근본 목적은 다르다는 것을 알아야 한다.</p>
<h3 id="oauth-10의-단점">OAuth 1.0의 단점</h3>
<ul>
<li>웹 애플리케이션이 아닌 애플리케이션에서는 사용하기 곤란하다</li>
<li>구현이 복잡하다.</li>
<li>절차가 복잡하여 OAuth 구현 라이브러리를 제작하기 어렵다.</li>
</ul>
<h3 id="oauth-20">OAuth 2.0</h3>
<ul>
<li>웹 애플리케이션이 아닌 애플리케이션 지원 강화</li>
<li>암호화가 필요 없다. HTTPS를 사용하고 HMAC을 사용하지 않는다.</li>
<li>Siganature 단순화 정렬과 URL 인코딩이 필요 없다.</li>
</ul>
<p>Access Token 갱신 OAuth 1.0에서 Access Token을 받으면 Access Token을 계속 사용할 수 있었다. 트위터의 경우에는 Access Token을 만료시키지 않는다. OAuth 2.0에서는 보안 강화를 위해 Access Token의 Life-time을 지정할 수 있도록 했다.</p>
<p>이외에도 OAuth 2.0에서 사용하는 용어 체계는 OAuth 1.0과 완전히 다르다. 같은 목적의 다른 프로토콜이라고 이해는 것이 좋다. 하지만 아직 최종안이 나오지 않았기 때문에, 현재로서는 OAuth 2.0의 특징만 파악하는 것으로도 충분할 듯 하다.</p>
<table>
<thead>
<tr>
<th align="center">비교</th>
<th align="left">OAuth 1.0</th>
<th align="left">OAuth 2.0</th>
</tr>
</thead>
<tbody><tr>
<td align="center">유효기간</td>
<td align="left">Access 토큰의 유효기간 없음(무제한)</td>
<td align="left">- Access 토큰 유효기간 부여 <br> - 만료 시 Refresh 토큰 이용하여 재발급</td>
</tr>
<tr>
<td align="center">클라이언트</td>
<td align="left">웹 서비스</td>
<td align="left">웹, 앱 등</td>
</tr>
</tbody></table>
<br>
<br>

<h2 id="kakao-developers-플랫폼-설정">Kakao Developers 플랫폼 설정</h2>
<h3 id="애플리케이션-생성">애플리케이션 생성</h3>
<p><a href="https://developers.kakao.com/">Kakao Developers</a> 에 접속해 로그인을 한다.</p>
<p>상단 메뉴 중에 <strong>&quot;내 애플리케이션&quot;</strong> 을 클릭한다.</p>
<p>가운데에 보이는 <strong>&quot;애플리케이션 추가하기&quot;</strong> 를 누른다.</p>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/4d8437f4-9280-4f18-8853-6bc7259bb81a/image.png" alt="">
<strong>&quot;앱 이름&quot;</strong> 과 <strong>&quot;사업자명&quot;</strong> 에 적당한 앱 이름을 입력하고, 저장을 누른다.</p>
<br>


<h3 id="서비스-도메인-주소-redirect-uri-등록">서비스 도메인 주소, REDIRECT URI 등록</h3>
<p>앱을 클릭한다.</p>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/721ef9a4-0f89-4358-935f-1096df4f7f61/image.png" alt=""></p>
<p>왼쪽 메뉴에서 <strong>&quot;플랫폼&quot;</strong> 을 클릭한다.</p>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/06afbff3-f1b8-44c4-8e65-d672808d7c19/2.png" alt="">
맨 아래에 있는 <strong>&quot;Web 플랫폼 등록&quot;</strong> 버튼을 누른다.</p>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/c2e45e25-28b2-4a31-b5ab-aabc5b1d930d/image.png" alt="">
사이트 도메인 주소를 입력하고, 저장을 누른다.
여기에서는 로컬 테스트용이기 때문에 <code>http://localhost:8080</code> 로 입력했다.</p>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/cc3268c7-7844-4a76-ab34-c2a3326f515d/3.png" alt=""></p>
<p>그러면 이와 같은 상태가 된다. 그리고 <strong>&quot;등록하러 가기&quot;</strong> 링크를 클릭한다.</p>
<p><strong>&quot;활성화 설정&quot;</strong> 의 상태 토글 버튼을 눌러 상태를 <strong>&quot;OFF&quot;</strong> 에서  <strong>&quot;ON&quot;</strong> 으로 바꾼다.</p>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/38051b55-2fca-40c6-9ac4-ce3de940b679/image.png" alt=""></p>
<p>그 아래에 있는 <strong>&quot;Redirect URI&quot;</strong> 등록 버튼을 누른다.</p>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/36f98d55-b605-41cb-a39d-5369a5048b9a/image.png" alt=""></p>
<p>현재는 로컬 테스트용이기 때문에 위와 같이 입력하고 저장을 누른다.</p>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/b6931a01-5c78-469a-a54f-b78f38f1ccd8/image.png" alt=""></p>
<p>그러면 이와 같은 상태가 된다.</p>
<br>

<h3 id="개인정보-제공-동의항목-설정">개인정보 제공 동의항목 설정</h3>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/9bba0464-7b64-4090-8f42-7743f305d2ad/image.png" alt=""></p>
<p>왼쪽 메뉴의 <strong>&quot;카카오 로그인&quot;</strong> 탭의 하위 항목들 중, <strong>&quot;동의항목&quot;</strong> 에 들어간다.</p>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/8e56d5ea-477e-48a7-925a-8d024ea717df/4.PNG" alt=""></p>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/16924383-f38e-4530-aa63-d4f8b953768b/5.PNG" alt=""></p>
<p>카카오로그인 시, 제공받고 싶은 개인정보들을 제공받을 수 있도록 설정한다.</p>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/f0d8d130-39e6-4546-8399-b33e27651643/6.PNG" alt=""></p>
<p>닉네임, 프로필 사진, 이메일, 연령대에 대해 설정을 완료한 모습이다.</p>
<br>

<h3 id="client-secret-발급">Client Secret 발급</h3>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/6e92900c-874e-4aa1-87f6-3bf652e0c8b6/image.png" alt=""></p>
<p>왼쪽 메뉴의 <strong>&quot;보안&quot;</strong> 탭에 들어간다.</p>
<p>이 설정은 안 해도 동작은 한다.</p>
<p>토큰 발급 시, 보안을 강화하기 위해 추가 확인하는 코드를 발급받는 설정이다. </p>
<p>보안은 중요하니 설정해보도록 하자.</p>
<p>가운데에 있는 <strong>&quot;코드 생성&quot;</strong> 버튼을 눌러 생성한다.</p>
<p>그리고 <strong>&quot;활성화 상태&quot;</strong> 를 <strong>&quot;사용&quot;</strong> 으로 바꾸자.</p>
<p>이제 Kakao developers에서의 설정은 끝났다!!</p>
<p>이를 사용하는 서비스 구현부를 만들어 보자.</p>
<br>

<h2 id="카카오-로그인oauth-20-흐름에-따른-구현">카카오 로그인(OAuth 2.0) 흐름에 따른 구현</h2>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/e42e9dbf-7432-4e17-9ee5-64cf7ad88b43/OAuth%20%EA%B3%BC%EC%A0%95.png" alt=""></p>
<p>사용자가 내 서비스에 접속한다.
<strong>&quot;카카오 간편 로그인&quot;</strong> 을 클릭한다.</p>
<h4 id="1-인증-코드-요청">1. 인증 코드 요청</h4>
<ul>
<li>REST API 키, REDIRECT URI를 같이 넘겨준다.</li>
</ul>
<pre><code class="language-http">GET /oauth/authorize?client_id={REST_API_KEY}&amp;redirect_uri={REDIRECT_URI}&amp;response_type=code HTTP/1.1
Host: kauth.kakao.com</code></pre>
<p>사용자에게 카카오 로그인 화면이 뜬다.
사용자가 로그인을 한다.
사용자가 내 서비스에 제공할 항목들을 선택 후, 확인 버튼을 누른다.</p>
<h4 id="2-인증-코드-전달">2. 인증 코드 전달</h4>
<ul>
<li>카카오 서버에서 REDIRECT URI 주소로 Redirect 한다.</li>
<li>GET 쿼리 파라미터로 <code>code={AUTHORIZE_CODE}</code> 를 보내준다.</li>
</ul>
<pre><code class="language-http">HTTP/1.1 302 Found
Content-Length: 0
Location: {REDIRECT_URI}?code={AUTHORIZE_CODE}</code></pre>
<h5 id="인증-코드를-받는-컨트롤러-코드">인증 코드를 받는 컨트롤러 코드</h5>
<pre><code class="language-Java">@RestController
public class OAuthControllerV1 {

    @GetMapping(&quot;/auth/kakao/callback&quot;)
    public String home(String code) {
        return &quot;인증 코드 : &quot; + code;
    }
}</code></pre>
<h4 id="3-인증-코드로-토큰-요청">3. 인증 코드로 토큰 요청</h4>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/74d13550-6902-423e-b783-77a4e38b26ab/image.png" alt=""></p>
<p>위의 파라미터들을 모두 포함해 토큰 요청을 보낸다.</p>
<h4 id="4-토큰-전달">4. 토큰 전달</h4>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/092752d4-efd0-402d-a89d-29a07d08f2eb/image.png" alt=""></p>
<p><strong>&quot;access_token&quot;</strong> 값을 받는다.</p>
<h5 id="인증-코드를-받은-후-위의-파라미터들을-모두-포함해-access-토큰-요청을-보내고-응답을-받는-코드">인증 코드를 받은 후, 위의 파라미터들을 모두 포함해 Access 토큰 요청을 보내고 응답을 받는 코드</h5>
<pre><code class="language-Java">@RequiredArgsConstructor
@RestController
public class OAuthControllerV2 {
    private final ObjectMapper objectMapper;

    @GetMapping(&quot;/auth/kakao/callback&quot;)
    public OAuthToken home(String code) {

        // 3. 인증 코드를 받은 후, 파라미터들을 포함해 토큰 요청
        MultiValueMap&lt;String, String&gt; params = new LinkedMultiValueMap&lt;&gt;();
        params.add(&quot;grant_type&quot;, &quot;authorization_code&quot;); // 고정값
        params.add(&quot;client_id&quot;, &quot;{REST API 키}&quot;);
        params.add(&quot;redirect_uri&quot;, &quot;http://localhost:8080/auth/kakao/callback&quot;);
        params.add(&quot;code&quot;, code);
        params.add(&quot;client_secret&quot;, &quot;{Client Secret}&quot;);

        // HttpHeader 오브젝트 생성
        HttpHeaders headersForAccessToken = new HttpHeaders();
        headersForAccessToken.add(&quot;Content-type&quot;, &quot;application/x-www-form-urlencoded;charset=utf-8&quot;);

        // HttpHeader와 HttpBody를 하나의 오브젝트에 담기
        HttpEntity&lt;MultiValueMap&lt;String, String&gt;&gt; kakaoTokenRequest = new HttpEntity&lt;&gt;(params, headersForAccessToken);

        // POST방식으로 key-value 데이터를 요청(카카오쪽으로)
        RestTemplate rt = new RestTemplate(); //http 요청을 간단하게 해줄 수 있는 클래스

        // 실제로 요청하기
        // Http 요청하기 - POST 방식으로 - 그리고 response 변수에 응답을 받음.
        ResponseEntity&lt;String&gt; accessTokenResponse = rt.exchange(
            &quot;https://kauth.kakao.com/oauth/token&quot;,
            HttpMethod.POST,
            kakaoTokenRequest,
            String.class
        );

        // JSON 응답을 객체로 변환
        OAuthToken oauthToken = null;
        try {
            oauthToken = objectMapper.readValue(accessTokenResponse.getBody(), OAuthToken.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        return oauthToken;
    }
}</code></pre>
<pre><code class="language-Java">@Getter
public class OAuthToken {
    private String access_token;
    private String token_type;
    private String refresh_token;
    private int expires_in;
    private String scope;
    private int refresh_token_expires_in;
}</code></pre>
<h4 id="5-access-토큰으로-api-호출">5. Access 토큰으로 API 호출</h4>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/260c4fa1-59e6-44e8-bd26-1447a7f8cb9e/image.png" alt=""></p>
<p>위의 요청으로 카카오 서버에게 사용자 정보를 요청한다.</p>
<p>카카오 서버는 <strong>&quot;6.토큰 유효성&quot;</strong> 을 확인한다.</p>
<h4 id="7-응답-전달">7. 응답 전달</h4>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/a84bd4e1-58b5-4db7-8daa-508cc7f9fdf5/image.png" alt=""></p>
<p>사용자의 정보를 응답으로 받는다.</p>
<h5 id="발급받은-access-토큰으로-api를-호출해서-사용자의-정보를-응답으로-받는-코드">발급받은 Access 토큰으로 API를 호출해서 사용자의 정보를 응답으로 받는 코드</h5>
<pre><code class="language-Java">@RequiredArgsConstructor
@RestController
public class OAuthControllerV3 {
    private final ObjectMapper objectMapper;

    @GetMapping(&quot;/auth/kakao/callback&quot;)
    public KakaoProfile home(String code) {

        // 3, 4 : 인증 코드를 받은 후, 위의 파라미터들을 모두 포함해 Access 토큰 요청을 보내고 응답을 받는 코드
        MultiValueMap&lt;String, String&gt; params = new LinkedMultiValueMap&lt;&gt;();
        params.add(&quot;grant_type&quot;, &quot;authorization_code&quot;); // 고정값
        params.add(&quot;client_id&quot;, &quot;{REST API 키}&quot;);
        params.add(&quot;redirect_uri&quot;, &quot;http://localhost:8080/auth/kakao/callback&quot;);
        params.add(&quot;code&quot;, code);
        params.add(&quot;client_secret&quot;, &quot;{Client Secret}&quot;);

        // HttpHeader 오브젝트 생성
        HttpHeaders headersForAccessToken = new HttpHeaders();
        headersForAccessToken.add(&quot;Content-type&quot;, &quot;application/x-www-form-urlencoded;charset=utf-8&quot;);

        // HttpHeader와 HttpBody를 하나의 오브젝트에 담기
        HttpEntity&lt;MultiValueMap&lt;String, String&gt;&gt; kakaoTokenRequest = new HttpEntity&lt;&gt;(params, headersForAccessToken);

        //POST방식으로 key-value 데이터를 요청(카카오쪽으로)
        RestTemplate rt = new RestTemplate(); //http 요청을 간단하게 해줄 수 있는 클래스

        // 실제로 요청하기
        // Http 요청하기 - POST 방식으로 - 그리고 response 변수의 응답을 받음.
        ResponseEntity&lt;String&gt; accessTokenResponse = rt.exchange(
            &quot;https://kauth.kakao.com/oauth/token&quot;,
            HttpMethod.POST,
            kakaoTokenRequest,
            String.class
        );

        OAuthToken oauthToken = null;
        try {
            oauthToken = objectMapper.readValue(accessTokenResponse.getBody(), OAuthToken.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        // 토큰 전달 받기 완료

        // 5, 6, 7 : 발급받은 Access 토큰으로 API를 호출해서 사용자의 정보를 응답으로 받는 코드
        HttpHeaders headersForRequestProfile = new HttpHeaders();
        headersForRequestProfile.add(&quot;Authorization&quot;, &quot;Bearer &quot; + Objects.requireNonNull(oauthToken).getAccess_token());
        headersForRequestProfile.add(&quot;Content-type&quot;, &quot;application/x-www-form-urlencoded;charset=utf-8&quot;);

        HttpEntity&lt;MultiValueMap&lt;String, String&gt;&gt; kakaoResourceProfileRequest = new HttpEntity&lt;&gt;(headersForRequestProfile);

        // Http 요청하기 - POST 방식으로 - 그리고 response 변수의 응답을 받음.
        ResponseEntity&lt;String&gt; resourceProfileResponse = rt.exchange(
            &quot;https://kapi.kakao.com/v2/user/me&quot;,
            HttpMethod.POST,
            kakaoResourceProfileRequest,
            String.class
        );

        KakaoProfile profile = null;
        try {
            profile = objectMapper.readValue(resourceProfileResponse.getBody(), KakaoProfile.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        return profile;
    }
}</code></pre>
<pre><code>@Getter
public class KakaoProfile {
    private int id;
    private String connected_at;
    private Properties properties;
    private KakaoAccount kakao_account;

    @Getter
    public static class Properties {
        private String nickname;
        private String profile_image;
        private String thumbnail_image;
    }

    @Getter
    public static class KakaoAccount {
        private Boolean profile_needs_agreement;
        private Profile profile;
        private Boolean has_email;
        private Boolean email_needs_agreement;
        private Boolean is_email_valid;
        private Boolean is_email_verified;
        private String email;
        private Boolean has_age_range;
        private Boolean age_range_needs_agreement;
        private Boolean has_birthday;
        private Boolean birthday_needs_agreement;
        private Boolean has_gender;
        private Boolean gender_needs_agreement;

        @Getter
        public static class Profile {
            private String nickname;
            private String thumbnail_image_url;
            private String profile_image_url;
        }
    }
}</code></pre><blockquote>
<p>참조 : 
<a href="https://d2.naver.com/helloworld/24942">https://d2.naver.com/helloworld/24942</a>
<a href="https://developers.kakao.com/">https://developers.kakao.com/</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[우아한테크코스 3기 백엔드 Lv2 [체스 미션] 학습로그]]></title>
            <link>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv2-%EC%B2%B4%EC%8A%A4-%EB%AF%B8%EC%85%98-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8</link>
            <guid>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv2-%EC%B2%B4%EC%8A%A4-%EB%AF%B8%EC%85%98-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8</guid>
            <pubDate>Mon, 21 Jun 2021 11:33:11 GMT</pubDate>
            <description><![CDATA[<h2 id="spring-di의존성-주입-적용---4">[Spring] DI(의존성 주입) 적용 - 4</h2>
<h3 id="내용">내용</h3>
<ul>
<li>기존 코드는 객체들이 서로 강하게 결합되어 있었다.</li>
<li>Spring Bean 생성자 주입 방식으로, 객체들 간의 결합도를 낮췄다.</li>
</ul>
<h3 id="태그">태그</h3>
<p>Spring, DI, OOP</p>
<br>

<h2 id="spring-web-mvc-적용---3">[Spring] Web MVC 적용 - 3</h2>
<h3 id="내용-1">내용</h3>
<ul>
<li>Spark Java를 제거하고, Spring Web MVC를 적용했다.</li>
<li>추상화가 잘 되어있어 Controller의 코드가 매우 간결하고 깔끔해졌다.</li>
</ul>
<h3 id="태그-1">태그</h3>
<p>Spring, MVC</p>
<br>

<h2 id="jdbc-jdbctemplate-적용---4">[JDBC] JdbcTemplate 적용 - 4</h2>
<h3 id="내용-2">내용</h3>
<ul>
<li><code>Mysql Jdbc Driver</code> 만 사용해서 작성했던 DAO 클래스들에 <code>JdbcTemplate</code> 를 적용했다.</li>
<li>한 단계 더 추상화 되어있어, 코드가 많이 줄어들고 깔끔해졌다.</li>
<li><code>queryForObject()</code> 로 DB에서 값을 조회할 때, 결괏값의 개수가 0개이면 <code>EmptyResultDataAccessException</code> 가 발생해, <code>try ~ catch</code> 문으로 직접 예외를 처리해야 하는 부분이 아쉬웠다.</li>
</ul>
<h3 id="태그-2">태그</h3>
<p>JDBC</p>
<br>

<h2 id="oop-도메인-분리---5">[OOP] 도메인 분리 - 5</h2>
<h3 id="내용-3">내용</h3>
<ul>
<li>기존 코드는 <strong>도메인</strong> 과 <strong>Spring과 관련된 Repository</strong> 들이 모두 얽혀있었다.</li>
<li>모든 도메인이 Spring에 의존하고 있었다.</li>
<li>테스트도 <strong>@SpringBootTest</strong> 를 사용해 Spring 자체를 실행시켜야 가능했다.</li>
<li>코드 리뷰어 <strong>김고래</strong> 의 피드백을 적용하면서, 도메인은 순수 자바 객체로 유지되어야 함을 깨달았다.</li>
<li>불가피하게 Spring을 띄워야 하는 Repository 관련 테스트를 제외하고, 도메인 관련 테스트들은 모두 순수 자바로만 실행할 수 있게 되었다. 테스트 실행 속도가 매우 빨라졌다.</li>
<li>도메인 자체만 테스트하다보니, 각 객체별로 작은 단위의 테스트를 매우 간단하게 할 수 있었다. 이전에는 모든 의존성을 알맞게 주입해 놓아야 테스트가 가능했다.</li>
<li>콘솔 게임은 Spring 없이 순수하게 자바로만 실행할 수 있게 되었다.</li>
</ul>
<h3 id="태그-3">태그</h3>
<p>OOP, Domain, Test, Spring</p>
<br>

<h2 id="test-in-memory-db-적용---3">[Test] In-Memory DB 적용 - 3</h2>
<h3 id="내용-4">내용</h3>
<ul>
<li>이전에는 테스트를 실제 DB를 통해서 했었다.</li>
<li>코드 리뷰어 <strong>김고래</strong> 의 피드백으로 Test들은 In-Memory DB인 H2 DB를 사용하게 했다.</li>
<li>테스트가 훨씬 가벼워지고, 외부 DB에 의존하지 않게 되었다. 또한, 속도도 빨라졌다.</li>
</ul>
<h3 id="태그-4">태그</h3>
<p>Test, DB</p>
<br>

<h2 id="spring-mvc-restcontrolleradvice---3">[Spring MVC] @(Rest)ControllerAdvice - 3</h2>
<h3 id="내용-5">내용</h3>
<ul>
<li>Controller와 Exception의 범위를 지정하면, Exception을 알아서 잡아준다.</li>
<li>try ~ catch 문을 없앨 수 있어, 비즈니스 로직이 매우 깔끔해진다.</li>
</ul>
<h3 id="태그-5">태그</h3>
<p>Spring, MVC, Exception</p>
<br>

<h2 id="http-rest-api---2">[HTTP] REST API - 2</h2>
<h3 id="내용-6">내용</h3>
<ul>
<li>삭제 요청을 POST &quot;/delete&quot; 로 했었다.</li>
<li>DELETE &quot;games/${gameId}&quot; 로 바꾸니, API가 더욱 명시적으로 되었다.</li>
</ul>
<h3 id="태그-6">태그</h3>
<p>HTTP, REST, API</p>
<br>

<h2 id="http-cookie---4">[HTTP] Cookie - 4</h2>
<h3 id="내용-7">내용</h3>
<ul>
<li>클라이언트에 값을 저장하고, 이후 요청마다 해당 값을 포함시키는데 적합한 쿠키를 사용했다.</li>
<li>한 번 쿠키를 발급해주면, 만료되기 전 까지 매 요청에 쿠키를 포함해 보내기 때문에 인증에 유용하다.</li>
</ul>
<h3 id="태그-7">태그</h3>
<p>HTTP, Cookie</p>
<br>

<h2 id="보안-hash-암호화---4">[보안] Hash 암호화 - 4</h2>
<h3 id="내용-8">내용</h3>
<ul>
<li>프로그래밍 요구사항에서 비밀번호를 클라이언트에 저장하고, 매 요청에 같이 보내라는 부분이 있었다.</li>
<li>이 방식은 보안에 매우 취약하기 때문에, Hash 암호화를 사용했다.</li>
<li>쿠키나 요청 데이터가 탈취당하더라도 Hash 함수로 암호화 되어있기 때문에, 암호 본문은 알 수 없어 보안에 유리하다.</li>
</ul>
<h3 id="태그-8">태그</h3>
<p>보안, Hash</p>
<br>
]]></description>
        </item>
        <item>
            <title><![CDATA[우테코에서 찾은 나만의 효과적인 공부법]]></title>
            <link>https://velog.io/@taehee-kim-dev/%EC%9A%B0%ED%85%8C%EC%BD%94%EC%97%90%EC%84%9C-%EC%B0%BE%EC%9D%80-%EB%82%98%EB%A7%8C%EC%9D%98-%ED%9A%A8%EA%B3%BC%EC%A0%81%EC%9D%B8-%EA%B3%B5%EB%B6%80%EB%B2%95</link>
            <guid>https://velog.io/@taehee-kim-dev/%EC%9A%B0%ED%85%8C%EC%BD%94%EC%97%90%EC%84%9C-%EC%B0%BE%EC%9D%80-%EB%82%98%EB%A7%8C%EC%9D%98-%ED%9A%A8%EA%B3%BC%EC%A0%81%EC%9D%B8-%EA%B3%B5%EB%B6%80%EB%B2%95</guid>
            <pubDate>Mon, 21 Jun 2021 00:03:38 GMT</pubDate>
            <description><![CDATA[<h1 id="효율적인-공부를-하자">효율적인 공부를 하자.</h1>
<br>

<h2 id="학자형-방식에서-야생형-방식으로">학자형 방식에서 야생형 방식으로.</h2>
<h4 id="과거">과거</h4>
<p>책의 첫 글자부터 마지막 글자까지 모두 읽어야 마음이 편했다. 일종의 강박이었다. 그러다 보니 시간도 오래 걸리고, 중요한 부분과 그렇지 않은 부분을 구분하기 어려웠다. 책을 모두 본 뒤에 <strong>&quot;내가 뭘 학습했지?&quot;</strong> 돌이켜 보면, 떠오르는 게 몇 개 없고 정리가 되지 않았다. 또한, 학습한 내용을 막상 사용하지 않으면 금세 다시 잊혀졌다. 그래서 학습했던 내용을 다시 찾아봐야 하는 경우가 자주 생겼다.</p>
<h4 id="현재">현재</h4>
<p>인프런에서 김영한 님의 강의를 들었는데, 야생형 방식의 학습을 추천하셨다. 이를 우테코에서 적용해 보았다. 야생형 방식이란, 미션 등의 프로젝트를 진행하면서 궁금한 부분이 생기면 해당 부분만 찾아서 학습하는 방식이다. 찾아본 내용의 세부 내용에 대해 궁금증이 생기면, <strong>&quot;왜?&quot;</strong> 라는 의문이 더 생기지 않을 때까지 깊이 파고든다. 이를 미션에 적용하고 개인 블로그에 정리하면 완전히 내 것이 된 느낌이다. 하지만 부분적으로 학습하는 것이기에, 전체적인 관점에서 이해하기 어렵다는 단점이 있다.</p>
<p>학자형 방식의 학습을 아예 안 하는 것은 아니다. 완전히 새로 접하는 기술(언어, 프레임워크 등)은 전체적으로 빠르게 훑어보고, 그다음에 세부적으로 파고든다. 전체적인 그림을 봐야 중요한 부분에 집중할 수 있고, 모르는 부분이 생겼을 때 어느 부분을 찾아봐야 하는지 알 수 있기 때문이다. 다만, 예전처럼 정독하지는 않는다. 책이나 인강을 빠르게 본다.</p>
<p>학습 방법에 정답은 없다. 필요한 지식, 상황에 따라 적절한 학습 방법을 선택해야 한다. 그것이 효과적이고 스스로 만족스럽다면 자신에게 맞는 학습법이라 생각한다.</p>
<br>

<h2 id="함께하는-것이-더-좋다">함께하는 것이 더 좋다.</h2>
<h4 id="과거-1">과거</h4>
<p>프로그래밍과 공부는 혼자 해야 효율적이라고 생각했다. 페어 프로그래밍을 한다고 했을 때, 진행 속도가 더딜까 걱정했다. 공부도 혼자 고민하고 자료를 찾아보면서 하는 게 더 정확한 정보를 찾을 수 있고 집중하기 좋다고 생각했다.</p>
<h4 id="현재-1">현재</h4>
<p>혼자 하는 미션도, 크루들과 같이한다. 집단지성의 위대함을 느끼고 있다. 여러 개의 뇌가 동시에 한 가지의 문제를 고민하니, 다양한 아이디어가 여러 가지 관점에서 빠르게 나온다. 혼자 할 때보다 2배 이상 빠른 것 같다. 최근 미션은 3명의 크루들과 함께 했다. 서로 모르는 부분들을 알려주며 상호보완적으로 코딩했다. </p>
<p>특정 지식에 대한 궁금증이 생기면, 크루들에게 물어본다. 주변에 훌륭한 크루들이 많아서 좋은 답변을 바로 들을 수 있다. 의문점이 생기면 다 같이 토론하며 답을 찾아 나간다. 여러 명이 함께 고민하니 다양한 관점에서 문제를 바라볼 수 있고, 기발한 아이디어가 많이 나온다. 속도 또한 매우 빠르다.</p>
<p>이제 혼자 코딩하거나 공부하면 허전하다. 루터에 출근하지 않는 날에도 카페에 모여서 같이 공부하고 코딩한다. 매우 효율적이고 즐겁다. 함께 하는 것의 즐거움을 알려준 우테코에게 감사하다.</p>
<br>

<h2 id="길게-보고-즐기자">길게 보고 즐기자.</h2>
<h4 id="과거-2">과거</h4>
<p>모르는 부분이 나오면 불안하고 빨리 알아야 할 것 같았다. 학습해야 할 지식이 너무 많아 막막하고 압박감이 들었다. 하나의 기술을 익히기도 버거운데, 계속 업데이트가 되어 내용이 바뀌고 새로운 기술이 등장한다.</p>
<h4 id="현재-2">현재</h4>
<p>위의 상황이 계속되니 점점 해탈하는 것 같다. 어차피 개발자로서 평생 공부해야 하니 마음을 편하게 가져야 한다는 생각이 문득 들었다. 지금도 과거의 습관이 남아있긴 하지만 아래와 같은 방식들로 연습하는 중이다. </p>
<ol>
<li>다른 크루들이 뛰어난 역량을 보일 때, 자신을 비교하며 조급해하거나 불안해하지 않기. </li>
<li>학습해야 할 것들이 너무 많아 압박이 느껴질 때, 마음을 편하게 먹고 새로운 것을 학습하는 것 자체에 흥미 가지기. </li>
<li>나만 모르는 것 같아 조바심이 들 때, <strong>&quot;몰라도 돼. 괜찮아.&quot;</strong> 라고 자신에게 말해주기.</li>
</ol>
<p>이와 같은 노력으로 조금씩 스트레스가 줄어드는 것 같다. 아직 갈 길이 멀고 많이 부족하지만, 꾸준한 노력을 하면 더 좋은 개발자로 성장할 수 있을 거라 믿는다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[우아한테크코스 3기 백엔드 Lv3 모의면접 대상 Lv2 학습로그 모음 - 답변]]></title>
            <link>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv3-%EB%AA%A8%EC%9D%98%EB%A9%B4%EC%A0%91-%EB%8C%80%EC%83%81-Lv2-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8-%EB%AA%A8%EC%9D%8C-%EB%8B%B5%EB%B3%80</link>
            <guid>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv3-%EB%AA%A8%EC%9D%98%EB%A9%B4%EC%A0%91-%EB%8C%80%EC%83%81-Lv2-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8-%EB%AA%A8%EC%9D%8C-%EB%8B%B5%EB%B3%80</guid>
            <pubDate>Sun, 20 Jun 2021 08:42:12 GMT</pubDate>
            <description><![CDATA[<h1 id="체스-미션">체스 미션</h1>
<h2 id="spring-di의존성-주입-적용---4">[Spring] DI(의존성 주입) 적용 - 4</h2>
<h3 id="내용">내용</h3>
<ul>
<li>기존 코드는 객체들이 서로 강하게 결합되어 있었다.</li>
<li>Spring Bean 생성자 주입 방식으로, 객체들 간의 결합도를 낮췄다.</li>
</ul>
<h3 id="태그">태그</h3>
<p>Spring, DI, OOP</p>
<br>

<h2 id="spring-web-mvc-적용---3">[Spring] Web MVC 적용 - 3</h2>
<h3 id="내용-1">내용</h3>
<ul>
<li>Spark Java를 제거하고, Spring Web MVC를 적용했다.</li>
<li>추상화가 잘 되어있어 Controller의 코드가 매우 간결하고 깔끔해졌다.</li>
</ul>
<h3 id="태그-1">태그</h3>
<p>Spring, MVC</p>
<br>

<h2 id="jdbc-jdbctemplate-적용---4">[JDBC] JdbcTemplate 적용 - 4</h2>
<h3 id="내용-2">내용</h3>
<ul>
<li><code>Mysql Jdbc Driver</code> 만 사용해서 작성했던 DAO 클래스들에 <code>JdbcTemplate</code> 를 적용했다.</li>
<li>한 단계 더 추상화 되어있어, 코드가 많이 줄어들고 깔끔해졌다.</li>
<li><code>queryForObject()</code> 로 DB에서 값을 조회할 때, 결괏값의 개수가 0개이면 <code>EmptyResultDataAccessException</code> 가 발생해, <code>try ~ catch</code> 문으로 직접 예외를 처리해야 하는 부분이 아쉬웠다.</li>
</ul>
<h3 id="태그-2">태그</h3>
<p>JDBC</p>
<br>

<p>A.</p>
<p>jdbcTemplate을 이용해서 select쿼리를 날리는 경우 .query 메소드를 사용하게 된다.</p>
<p>그런데 .query 메소드의 경우 List형태로 리턴을 해주기 때문에 .queryForObject 메소드를 사용하는 경우도 있다.</p>
<p>다만 .queryForObject 메소드의 경우에는, row가 정상적으로 1개 나왔을 경우에는 문제가 없는데, row가 아예 나오지 않는 경우에 대해 exception을 떨구게 된다.</p>
<p>이곳 저곳 구글링을 해보면, try catch 문으로 exception을 캐치하여 null을 리턴하도록 하라고는 하는데, try catch 문의 경우 성능 저하의 원인이 될 수 있기 때문에 웬만해선 사용을 권장하지 않는다.</p>
<p>해결방안은 .query메소드를 사용하되, 단일 행이 나올 것이라고 예상되는 구문에서는 DataAccessUtils.singleResult나 DataAccessUtils.uniqueResult를 이용하여 List형태를 Object형태로 리턴하게끔 하면 된다.</p>
<p>메소드 구현체 내부를 들어가보면 알겠지만, .query 메소드의 결과로 나온 List의 사이즈를 체크하여, 사이즈가 0이면 null을 리턴하고, 사이즈가 0이 아니면 첫번재 element를 끄집어와서 리턴을 해주는 방식으로 되어 있다.</p>
<blockquote>
<p>출처: <a href="https://hakurei.tistory.com/15">https://hakurei.tistory.com/15</a> [Reimu&#39;s Development Blog]</p>
</blockquote>
<br>

<h2 id="oop-도메인-분리---5">[OOP] 도메인 분리 - 5</h2>
<h3 id="내용-3">내용</h3>
<ul>
<li>기존 코드는 <strong>도메인</strong> 과 <strong>Spring과 관련된 Repository</strong> 들이 모두 얽혀있었다.</li>
<li>모든 도메인이 Spring에 의존하고 있었다.</li>
<li>테스트도 <strong>@SpringBootTest</strong> 를 사용해 Spring 자체를 실행시켜야 가능했다.</li>
<li>코드 리뷰어 <strong>김고래</strong> 의 피드백을 적용하면서, 도메인은 순수 자바 객체로 유지되어야 함을 깨달았다.</li>
<li>불가피하게 Spring을 띄워야 하는 Repository 관련 테스트를 제외하고, 도메인 관련 테스트들은 모두 순수 자바로만 실행할 수 있게 되었다. 테스트 실행 속도가 매우 빨라졌다.</li>
<li>도메인 자체만 테스트하다보니, 각 객체별로 작은 단위의 테스트를 매우 간단하게 할 수 있었다. 이전에는 모든 의존성을 알맞게 주입해 놓아야 테스트가 가능했다.</li>
<li>콘솔 게임은 Spring 없이 순수하게 자바로만 실행할 수 있게 되었다.</li>
</ul>
<h3 id="태그-3">태그</h3>
<p>OOP, Domain, Test, Spring</p>
<br>

<h2 id="test-in-memory-db-적용---3">[Test] In-Memory DB 적용 - 3</h2>
<h3 id="내용-4">내용</h3>
<ul>
<li>이전에는 테스트를 실제 DB를 통해서 했었다.</li>
<li>코드 리뷰어 <strong>김고래</strong> 의 피드백으로 Test들은 In-Memory DB인 H2 DB를 사용하게 했다.</li>
<li>테스트가 훨씬 가벼워지고, 외부 DB에 의존하지 않게 되었다. 또한, 속도도 빨라졌다.</li>
</ul>
<h3 id="태그-4">태그</h3>
<p>Test, DB</p>
<br>

<h2 id="spring-mvc-restcontrolleradvice---3">[Spring MVC] @(Rest)ControllerAdvice - 3</h2>
<h3 id="내용-5">내용</h3>
<ul>
<li>모든 컨트롤러에서 발생하는 예외를 catch할 수 있다.</li>
<li>패키지, Controller, Exception의 범위를 지정할 수도 있다.</li>
<li>try ~ catch 문을 없앨 수 있어, 비즈니스 로직이 매우 깔끔해진다.</li>
</ul>
<h3 id="태그-5">태그</h3>
<p>Spring, MVC, Exception</p>
<br>

<h3 id="exceptionhandler">@ExceptionHandler</h3>
<p>@ExceptionHandler를 등록한 Controller에만 적용된다. 다른 Controller에서 명시되어있는 예외가 발생하더라도 예외를 처리할 수 없다.</p>
<h3 id="controlleradvice">@ControllerAdvice</h3>
<p>@ExceptionHandler가 하나의 클래스에 대한 것이라면, @ControllerAdvice는 모든 @Controller 즉, 전역에서 발생할 수 있는 예외를 잡아 처리해주는 annotation이다.</p>
<h3 id="restcontrolleradvice">@RestControllerAdvice</h3>
<p>@ControllerAdvice와 동일한 역할 즉, 모든 Controller에서 발생하는 예외를 잡아 핸들링 하는 기능을 수행하면서 @ResponseBody를 통해 객체를 리턴할 수도 있다는 얘기다.</p>
<p>ViewResolver를 통해서 예외 처리 페이지로 리다이렉트 시키려면 @ControllerAdvice만 써도 되고, API서버여서 에러 응답으로 객체를 리턴해야한다면 @ResponseBody 어노테이션이 추가된 @RestControllerAdvice를 적용하면 되는 것이다.</p>
<p>@RestController에서 예외가 발생하든 @Controller에서 예외가 발생하든 @ControllerAdvice + @ExceptionHandler 조합으로 다 캐치할 수 있고 @ResponseBody의 필요 여부에 따라 적용하면 된다는 것이다.</p>
<p><code>@ControllerAdvice + @ResponseBody = @RestControllerAdvice</code></p>
<blockquote>
<p>출처: <a href="https://jeong-pro.tistory.com/195">https://jeong-pro.tistory.com/195</a> [기본기를 쌓는 정아마추어 코딩블로그]</p>
</blockquote>
<br>

<h2 id="http-rest-api---2">[HTTP] REST API - 2</h2>
<h3 id="내용-6">내용</h3>
<ul>
<li>삭제 요청을 POST &quot;/delete&quot; 로 했었다.</li>
<li>DELETE &quot;games/${gameId}&quot; 로 바꾸니, API가 더욱 명시적으로 되었다.</li>
</ul>
<h3 id="태그-6">태그</h3>
<p>HTTP, REST, API</p>
<br>

<h2 id="http-cookie---4">[HTTP] Cookie - 4</h2>
<h3 id="내용-7">내용</h3>
<ul>
<li>클라이언트에 값을 저장하고, 이후 요청마다 해당 값을 포함시키는데 적합한 쿠키를 사용했다.</li>
<li>한 번 쿠키를 발급해주면, 만료되기 전 까지 매 요청에 쿠키를 포함해 보내기 때문에 인증에 유용하다.</li>
</ul>
<h3 id="태그-7">태그</h3>
<p>HTTP, Cookie</p>
<br>

<h2 id="보안-hash-암호화---4">[보안] Hash 암호화 - 4</h2>
<h3 id="내용-8">내용</h3>
<ul>
<li>프로그래밍 요구사항에서 비밀번호를 클라이언트에 저장하고, 매 요청에 같이 보내라는 부분이 있었다.</li>
<li>이 방식은 보안에 매우 취약하기 때문에, Hash 암호화를 사용했다.</li>
<li>쿠키나 요청 데이터가 탈취당하더라도 Hash 함수로 암호화 되어있기 때문에, 암호 본문은 알 수 없어 보안에 유리하다.</li>
</ul>
<h3 id="태그-8">태그</h3>
<p>보안, Hash</p>
<br>


<h1 id="배포운영-인프라">배포/운영 인프라</h1>
<h2 id="운영-bastion-서버-적용---3">[운영] Bastion 서버 적용 - 3</h2>
<h3 id="내용-9">내용</h3>
<ul>
<li>일반 서비스 접속용 경로와 별도로 관리자용 접속 경로인 Bastion 서버를 구성함.</li>
<li>서비스 접속 경로의 책임과 관리자 접속 경로의 책임을 분리함.</li>
<li>Bastion 서버가 피해를 보더라도 이 부분만 재구성하면 되므로, 서비스 피해를 최소화 할 수 있음.</li>
<li>DDos 공격을 받고 있다면 일반 서비스 접속용 경로로 접속할 수 없기 때문에, 관리자는 Bastion 서버를 통해 접속해야 함.</li>
</ul>
<h3 id="태그-9">태그</h3>
<p>운영, 보안</p>
<br>

<p>A. </p>
<p>일반 서비스 접속용 경로와 별도로 관리자용 접속 경로인 Bastion 서버를 구성했습니다.</p>
<p>이를 통해 서비스 접속 경로의 책임과, 관리자 접속 경로의 책임을 분리했습니다.</p>
<p>모든 서버에 동일한 수준의 보안을 설정하면, Auto-Scaling 등의 확장을 위한 관리 포인트가 늘어나, 일반적으로 보안의 일정 부분을 포기하는 결정을 하게 됩니다.</p>
<p>Bastion 서버를 두면, 이러한 보안 문제와 확장성 문제를 모두 해결할 수 있습니다.</p>
<p>Bastion 서버가 공격을 받았을 때, 이 부분만 재구성하면 되므로 서비스 피해를 최소화 할 수 있습니다.</p>
<p>DDos 공격을 받고 있다면 일반 서비스 접속 경로로 접속 할 수 없습니다. 이때, 관리자용 접속 경로인 Bastion 서버를 구성해 두었다면 이를 통해 접속할 수 있습니다.</p>
<br>

<h2 id="운영-thread-dump-모니터링---2">[운영] Thread Dump 모니터링 - 2</h2>
<h3 id="내용-10">내용</h3>
<ul>
<li>Thread들의 실시간 상태를 모니터링 할 수 있다.</li>
<li>에러가 발생했을 경우, 디버깅에 유리하다.</li>
</ul>
<h3 id="태그-10">태그</h3>
<p>운영, Thread</p>
<h3 id="thread-dump">Thread Dump</h3>
<blockquote>
<p>A thread dump is a snapshot of the state of all the threads of a Java process.</p>
</blockquote>
<p>Thread Dump 란, Java 프로세스의 모든 Thread들의 스냅샷(상태를 사진처럼 찍은 것)을 말한다.</p>
<h3 id="snapshot">Snapshot</h3>
<blockquote>
<p>In computer systems, a snapshot is the state of a system at a particular point in time.</p>
</blockquote>
<p>Snapshot 이란, 특정 시점의 시스템 상태를 말한다.</p>
<blockquote>
<p>Snapshot 이란, 특정 시점의 시스템 상태를 사진처럼 찍은 것을 말한다.</p>
</blockquote>
<p>A. </p>
<p>Thread Dump란, Java 프로세스의 모든 Thread들의 스냅샷을 말합니다.</p>
<p>스냅샷이란, 특정 시점의 시스템 상태를 말합니다.</p>
<p>즉 Thread Dump란, Java 프로세스의 모든 Thread들의 특정 시점의 상태를 사진처럼 찍은 것을 말합니다.</p>
<br>

<h2 id="log-logback-적용---4">[Log] LogBack 적용 - 4</h2>
<h3 id="내용-11">내용</h3>
<ul>
<li>Logger의 이름별로 다른 파일에 로그가 쌓이도록 해, 로그 관리가 용이해짐.</li>
</ul>
<h3 id="태그-11">태그</h3>
<p>Log</p>
<br>

<h2 id="aws-cloudwatch---4">[AWS] CloudWatch - 4</h2>
<h3 id="내용-12">내용</h3>
<ul>
<li>EC2의 네트워크 상태, CPU, 메모리/디스크 사용률 등을 CloudWatch 대시보드에서 시각적으로 확인할 수 있음.</li>
<li>로그 파일들을 CloudWatch에 연동시켜 EC2에 접속하지 않고도 AWS 홈페이지에서 파일별 로그들을 편리하게 확인할 수 있음.</li>
</ul>
<h3 id="태그-12">태그</h3>
<p>운영, AWS, CloudWatch</p>
<h2 id="container-docker-적용---5">[Container] Docker 적용 - 5</h2>
<h3 id="내용-13">내용</h3>
<ul>
<li>MySQL을 Docker로 띄웠다.</li>
<li>Docker만 설치되어 있다면, 배포 환경과 완전히 독립적으로 Container를 실행시킬 수 있었다.</li>
</ul>
<h3 id="태그-13">태그</h3>
<p>배포, Container, Docker</p>
<br>

<hr>
<br>

<h1 id="지하철-미션">지하철 미션</h1>
<h2 id="test-atdd---5">[Test] ATDD - 5</h2>
<h3 id="내용-14">내용</h3>
<ul>
<li>API 스펙 요구사항에 대해 ATDD를 진행했다.</li>
<li>테스트가 모두 통과할 때 마다, API 스펙 요구사항을 충족하고 있음을 확신할 수 있었다.</li>
<li>테스트가 추상적이어서, 리팩토링에 따른 테스트코드 수정이 거의 불필요했다.</li>
</ul>
<h3 id="태그-14">태그</h3>
<p>Test, ATDD</p>
<br>

<p>A.
ATDD는 인수 테스트를 먼저 작성한 다음, 기능 개발을 하는 방식입니다.</p>
<p>인수 테스트는 시스템의 인수 결정을 위해 사용자 입장에서 요구사항을 테스트하는 것을 말합니다.</p>
<p>블랙박스 테스트로, 시스템 내부 구현은 모르는 상태로 API 요청과 응답으로만 테스트합니다.</p>
<blockquote>
<p>출처: <a href="https://needjarvis.tistory.com/446">고객이 확인하는, 인수 테스트(Acceptance Test)</a></p>
</blockquote>
<br>

<h2 id="인증-jwt---5">[인증] JWT - 5</h2>
<h3 id="내용-15">내용</h3>
<ul>
<li>로그인 인증에 JWT를 사용했다.</li>
<li>상태 정보(세션 등)를 서버에 저장하지 않아도 돼, 서버 부담을 줄일 수 있었다.</li>
</ul>
<h3 id="태그-15">태그</h3>
<p>인증, JWT</p>
<br>

<p>A.</p>
<h3 id="세션-인증-stateful">세션 인증 (Stateful)</h3>
<p>세션을 사용하는 방식은 Stateful한 방식입니다.
인증 상태를 서버 메모리나 데이터베이스에 보관하고 유지합니다.</p>
<p>이 방식은, 로그인 중인 사용자가 많아질수록 서버에 과부하가 생깁니다.</p>
<p>서버 확장을 해야할 때, 인증 상태 유지와 호환을 고려해야 하기 때문에 Stateful한 세션 방식은 서버 확장에 불리합니다.</p>
<p>세션의 기반이 되는 쿠키는 단일 도메인 및 서브 도메인에서만 작동하도록 설계되어있어, CORS 이슈를 관리해주어야 하는 번거로움이 있습니다.</p>
<h3 id="토큰-기반-인증-시스템-stateless">토큰 기반 인증 시스템 (Stateless)</h3>
<p>토큰 기반 인증 시스템은 Stateless한 방식입니다.</p>
<p>서버가 인증 상태를 저장하지 않습니다.</p>
<p>클라이언트 요청에 포함되어있는 인증 토큰만을 가지고 인증에 대한 정보를 얻고 처리합니다.</p>
<p>이렇게 상태가 없는 경우 클라이언트와 서버간의 연결고리가 없기 때문에 서버의 확장성(Scalability)이 높아집니다.</p>
<h3 id="jwtjson-web-token">JWT(JSON Web Token)</h3>
<p>JWT는 토큰 기반 인증 시스템의 구현체 이며, 웹 표준입니다.</p>
<p>JSON 데이터 포맷을 기반으로 한 웹 토큰입니다.</p>
<p>모든 정보를 자체적으로 지니고 있습니다.</p>
<ul>
<li>Header : 토큰에 대한 기본정보</li>
<li>Payload : 전달할 정보</li>
<li>Signature : 토큰이 검증에 대한 정보</li>
</ul>
<p>HTTP 헤더에 넣어서 전달할 수 있고, URL의 파라미터로 전달할 수도 있습니다.</p>
<br>

<blockquote>
<p>참조 : <a href="https://velopert.com/2350">[JWT] 토큰(Token) 기반 인증에 대한 소개</a></p>
</blockquote>
<h3 id="base64란">Base64란?</h3>
<p>인코딩 : 데이터의 표준화, 보안, 처리속도 향상, 저장 공간 절약 등을 위해 다른 형태로 변환하는 방식을 말합니다.</p>
<p>이미지, 동영상, 오디오 전송에 많이 활용됩니다.</p>
<p>인코딩의 반대는 디코딩입니다.</p>
<p>Base64 : 8비트 이진 데이터(실행파일, ZIP파일 등)를 <strong>문자코드에 영향을 받지 않는 공통 ASCII 영역의 문자들로 바꾸는 인코딩 방식</strong>을 말합니다.</p>
<p>6bit당 2bit의 오버헤드가 발생해, 전송 데이터의 크기가 약 33% 정도 늘어납니다.</p>
<p>일반 ASCII 문자로 인코딩하면 다음과 같은 시스템 비독립성의 문제들이 발생할 수 있습니다.</p>
<ul>
<li>ASCII는 7 bits Encoding인데 나머지 1bit를 처리하는 방식이 시스템별로 다릅니다.</li>
<li>일부 제어 문자(e.g. Line ending)의 경우 시스템 별로 다른 코드값을 가집니다.</li>
</ul>
<p>위와 같은 문제로 일반 ASCII 문자는 다양한 시스템 간의 데이터를 전달하기에 안전하지 않습니다. </p>
<p>Base64는 이러한 시스템 비독립성 문제를 해결하기 위해 ASCII 문자들 중 제어 문자와 일부 특수문자를 제외한 64개의 안전한 출력 문자만을 사용합니다.
(*안전한 출력 문자란, 문자 코드에 영향을 받지 않는 공통 ASCII를 의미)</p>
<p>즉, Base64는 HTML 또는 Email과 같이 문자를 위한 Media에 Binary Data를 포함해야 될 필요가 있을 때, 포함된 Binary Data가 <strong>시스템 독립적으로 동일하게 전송 또는 저장되는 걸 보장</strong>하기 위해 사용합니다.</p>
<blockquote>
<p>출처: <a href="https://devuna.tistory.com/41">[Base 64] Base64이란 무엇일까? / Base64 사용 이유와 인코딩과 디코딩</a></p>
</blockquote>
<h3 id="url-safe-base64">URL Safe Base64</h3>
<p>Base64에서 62, 63번의 인코딩 문자값을 보면 각각 <code>+</code>, <code>/</code> 입니다. 이는 연산자이기 때문에 데이터를 다룰 때 오류를 일으킬 수 있습니다. 또한, <code>=</code>는 padding 문자에 해당하는데, 이는 URL에서 기존에 사용되고 있는 문자입니다. 따라서 URL Safe 한 base64의 62, 63번은 <code>-</code>, <code>_</code>로 대체되었고, <code>=</code>는 생략하거나 특정 라이브러리는 <code>.</code>로 대체합니다.</p>
<blockquote>
<p>출처: 
  <a href="https://babyprogram.tistory.com/50">base64 vs base64url safe 차이</a>
  <a href="https://www.base64url.com">base64url.com</a></p>
</blockquote>
<br>

<h2 id="spring-interceptor---3">[Spring] Interceptor - 3</h2>
<h3 id="내용-16">내용</h3>
<ul>
<li>로그인 인증이 필요한 URI 요청에 Interceptor를 걸고, JWT 유효성 검사 로직을 추가했다.</li>
<li>JWT 검사 책임을 Interceptor로 분리할 수 있었다.</li>
</ul>
<h3 id="태그-16">태그</h3>
<p>Spring, Interceptor</p>
<br>

<p><img src="https://images.velog.io/images/taehee-kim-dev/post/e9117800-7d91-4452-b007-9f2fb9fd4129/image.png" alt=""></p>
<h3 id="filter-vs-interceptor">Filter vs Interceptor</h3>
<p>A.
Filter는 Spring Context의 외부, DispatcherServlet 앞에 존재합니다. 요청이나 응답 내용의 체크, 변경(인코딩 변환 등)등을 할 수 있습니다.</p>
<p>Interceptor는 Spring Context의 내부에 존재합니다.
DispatcherServlet이 Controller(Handler)를 호출하는 시점의 전, 후에 끼어들어 Controller(Handler)의 요청과 응답을 가로채서(Intercept) 처리합니다.</p>
<p>Spring Context의 내부에 존재하기 때문에, Spring의 모든 Bean 객체에 접근할 수 있습니다.</p>
<p>Interceptor는 여러 개를 사용할 수 있습니다. 로그인 체크, 권한 체크, 프로그램 실행시간 계산 작업, 로그 확인 등에 쓰입니다.</p>
<p>Interceptor methods</p>
<ul>
<li>preHandle() - 컨트롤러 메서드 실행 전</li>
<li>postHanle() - 컨트롤러 메서드 실행 후, view페이지 렌더링 전</li>
<li>afterCompletion() - view 페이지가 렌더링 되고 난 후</li>
</ul>
<br>

<blockquote>
<p>출처 : <a href="https://goddaehee.tistory.com/154">[Spring] Filter, Interceptor, AOP 차이 및 정리</a></p>
</blockquote>
<br>

<h2 id="spring-custom-annotation-argumentresolver---3">[Spring] Custom Annotation, ArgumentResolver - 3</h2>
<h3 id="내용-17">내용</h3>
<ul>
<li>Custom Annotation을 ArgumentResolver에 등록해, 해당 Annotation이 메소드의 매개변수 앞에 붙어있다면 매개변수에 현재 로그인 되어있는 Member객체를 넣어주도록 했다.</li>
<li>로그인 사용자 사전 조회 책임을 <code>Custom Annotation + ArgumentResolver</code>로 분리할 수 있었다.</li>
</ul>
<h3 id="태그-17">태그</h3>
<p>Spring, Annotation, ArgumentResolver</p>
<br>

<h2 id="oop-layer와-domain의-책임-분리---4">[OOP] Layer와 Domain의 책임 분리 - 4</h2>
<h3 id="내용-18">내용</h3>
<ul>
<li>Service 레이어에 모든 비즈니스 로직을 작성하다보니 Service 클래스가 매우 커지고, 도메인과 Service 레이어의 책임 분할이 되지 않았다.</li>
<li>비즈니스 로직은 모두 도메인 내부로 옮기고, Service 레이어에서는 도메인의 비즈니스 로직 실행 순서만 보장하도록 해, 각각의 책임을 분리했다.</li>
</ul>
<h3 id="태그-18">태그</h3>
<p>OOP, Domain, Service</p>
<br>

<h2 id="jdbc-namedparameterjdbctemplate-적용---3">[JDBC] NamedParameterJDBCTemplate 적용 - 3</h2>
<h3 id="내용-19">내용</h3>
<ul>
<li>파라미터를 순서 숫자가 아닌 컬럼명 문자열로 표기해 더욱 직관적이다. 유지보수에 유리하다.</li>
</ul>
<h3 id="태그-19">태그</h3>
<p>JDBC</p>
<br>

<h2 id="암호화-bcrypt---5">[암호화] BCrypt - 5</h2>
<h3 id="내용-20">내용</h3>
<ul>
<li>리뷰어 미립이 Hash 암호화에 대한 학습 키워드를 제공해줬다.</li>
<li>같은 원본 데이터에 대해 암호화를 할 때마다 매번 다른 Hash 결과값이 나오는 BCrypt에 대해 학습했다.</li>
</ul>
<h3 id="태그-20">태그</h3>
<p>암호화</p>
<br>

<p>A.
BCrypt는 동일한 원본 데이터에 대해 암호화를 할 때 마다 매번 다른 Hash 결과값이 나오는 암호 해시 함수입니다.</p>
<p>일반적인 Hash 함수는 원본 데이터가 동일하면, Hash 결과값도 동일합니다.</p>
<p>이럴 경우, Rainbow table 공격에 취약합니다.</p>
<p>Rainbow table은 원본 데이터와 Hash 결과값의 짝을 미리 저장해놓은 것을 말합니다.</p>
<p>이를 사용해 Hash 암호화 된 비밀번호로부터 원본 비밀번호를 추출합니다.</p>
<p>이를 Rainbow table 공격이라고 합니다.</p>
<p>BCrpyt 알고리즘은 동일한 원본 데이터라도 Hash 결과값이 매번 다르기 때문에, Rainbow table 공격을 방지할 수 있습니다.</p>
<p>BCrypt의 이러한 작동 방식이 가능한 이유는, Salt를 사용하기 때문입니다.</p>
<p>말 뜻 그대로 소금처럼 암호화를 할 때 추가적인 데이터를 첨가해, 매 번 결과값이 다르게 합니다.</p>
<p>BCrpyt 알고리즘은 Spring Security의 PasswordEncoder의 비밀번호 암호화 기본 전략입니다.</p>
<p>비밀번호 일치 검사를 할 때, 로그인 요청으로 들어온 비밀번호를 BCrpyt 알고리즘으로 암호화 합니다. 이 때, DB에 저장되어있는 암호화된 비밀번호를 Salt로 사용합니다. 이 결과가 DB에 저장되어있던 암호화된 비밀번호와 일치하면, 로그인 요청으로 들어온 비밀번호는 맞는 비밀번호임이 확인됩니다.</p>
<br>

<h2 id="db-transaction-격리-수준---5">[DB] Transaction 격리 수준 - 5</h2>
<h3 id="내용-21">내용</h3>
<ul>
<li>Transaction 격리 수준에 대해 공부하고, 지하철 구간 수정 작업 단위에는 얼만큼의 격리 수준으로 Transaction을 걸어야 하는지 리뷰어 재연링과 함께 토론하며 고민했다.</li>
</ul>
<h3 id="태그-21">태그</h3>
<p>DB, Transaction</p>
<br>
A.
지하철 구간 업데이트를 서로 다른 사용자가 동시에 할 때 일어날 수 있는 동시성 문제에 대해 고민하면서 트랜잭션 격리수준에 대해 공부하고 리뷰어와 이야기를 나눴습니다.

<p>노선의 모든 구간들을 SELECT 한 뒤에, 구간 변경 요청의 유효성을 검사하고, DB를 업데이트 합니다.</p>
<p>여러 사용자가 이와 같은 요청을 동시에 하면 DB에 저장되는 지하철 구간이 꼬일 수 있습니다.</p>
<p>이를 해결하기 위해 DB 트랜잭션 격리수준을 어떻게 설정해야 할지 고민했습니다.</p>
<p>트랜잭션 격리수준은, 동시에 여러 트랜잭션들이 처리될 때, 각 트랜잭션끼리 얼마나 서로 격리되어 있는지를 나타내는 것입니다.</p>
<p>구체적으로 말해서, 특정 트랜잭션이 다른 트랜잭션이 변경한 데이터를 볼 수 있도록 허용할지 말지를 결정하는 것입니다.</p>
<h4 id="read-uncommitted">READ UNCOMMITTED</h4>
<p>다른 트랜잭션은 아직 Commit되지 않은 변경내용도 조회할 수 있다.</p>
<ol>
<li>A 트랜잭션이 1번 크루의 나이를 10살에서 100살로 변경했다. 아직 커밋은 하지 않았다.</li>
<li>B 트랜잭션이 1번 크루의 나이를 조회했다. 100살로 조회됐다. 이를 Dirty Read라고 한다.</li>
<li>만약 A 트랜잭션에 문제가 발생해 Rollback이 됐다고 하자.</li>
<li>B 트랜잭션은 1번 크루가 여전히 100살이라고 생각하고 로직을 수행한다.</li>
</ol>
<p>이런식으로 데이터 정합성에 문제가 많으므로, RDBMS 표준에서는 격리수준으로 인정하지도 않는다.</p>
<h4 id="read-committed-oracle-기본-설정">READ COMMITTED (Oracle 기본 설정)</h4>
<p>다른 트랜잭션은 Commit된 변경내용만 조회할 수 있다.</p>
<p>위의 경우를 다시 해보면,</p>
<p>B 트랜잭션이 1번 크루의 나이를 조회한 결과는 10살이다.
A 트랜잭션에서 Commit을 한 후에 B 트랜잭션에서 1번 크루의 나이를 조회해야 100살로 조회된다.</p>
<p>이 경우 <code>NON-REPEATABLE READ</code> 부정합 문제가 발생할 수 있다.</p>
<ol>
<li>A 트랜잭션이 1번 크루의 나이를 10살에서 100살로 변경했다. 아직 커밋은 하지 않았다.</li>
<li>B 트랜잭션이 1번 크루의 나이를 조회했다. 10살로 조회됐다.</li>
<li>A 트랜잭션이 Commit 됐다.</li>
<li>B 트랜잭션이 1번 크루의 나이를 다시 조회했다. 100살이 조회됐다.</li>
</ol>
<p>이는 하나의 트랜잭션 내에서 SELECT를 여러 번 했을 경우, 항상 같은 결과가 조회되어야 한다는 <code>REPEATABLE READ</code> 정합성에 어긋난다.</p>
<h4 id="repeatable-read-mysql-기본-설정">REPEATABLE READ (MySQL 기본 설정)</h4>
<p>트랜잭션이 시작되기 전에 Commit된 내용만 조회할 수 있다.</p>
<p>위에서 언급한 <code>NON-REPEATABLE READ</code> 부정합 문제가 발생하지 않는다.</p>
<p>이 격리 수준 이하에서는 Phantom Read 문제가 발생한다.</p>
<h4 id="serializable">SERIALIZABLE</h4>
<p>가장 단순하고 가장 엄격한 격리수준이다.
격리수준이 SERIALIZABLE일 경우 읽기 작업에도 공유 잠금을 설정하게 되고, 이렇게 되면 동시에 다른 트랜잭션에서 이 레코드를 변경하지 못하게 된다.
이러한 특성 때문에 동시처리 능력이 다른 격리수준보다 떨어지고, 성능저하가 발생한다.</p>
<br>

<h2 id="oop-원시값-객체-포장---2">[OOP] 원시값 객체 포장 - 2</h2>
<h3 id="내용-22">내용</h3>
<ul>
<li>지하철 요금 원시값을 <code>Fare</code> 값 객체로 포장했다.</li>
<li>생성자에서 값 자체의 유효성 검증을 할 수 있었다.</li>
<li>비즈니스 로직을 객체 내부에서 처리할 수 있었다.</li>
</ul>
<h3 id="태그-22">태그</h3>
<p>OOP</p>
<br>

<h2 id="배포-배포-자동화---4">[배포] 배포 자동화 - 4</h2>
<h3 id="내용-23">내용</h3>
<ul>
<li>배포 자동화가 적용되지 않은 상태에서는 <code>github에서 최신 코드 가져오기 -&gt; 빌드 -&gt; 기존에 실행중인 애플리케이션 종료 -&gt; 새로운 버전의 애플리케이션 실행</code> 의 과정을 일일이 명령어를 쳐서 실행해야 했다.</li>
<li>이 과정 전체를 shell 스크립트로 작성했다.</li>
<li>해당 shell 스크립트를 실행시키기만 하면 새로운 버전의 애플리케이션이 자동으로 배포되어 배포가 매우 편리해졌다.</li>
</ul>
<h3 id="태그-23">태그</h3>
<p>배포</p>
<br>

<h2 id="web-cors-정책---3">[Web] CORS 정책 - 3</h2>
<h3 id="내용-24">내용</h3>
<ul>
<li>프론트 서버와 백엔드 서버가 따로 운영되면서, <code>CORS</code> 정책 위반 에러가 발생했다.</li>
<li><code>Access-Control-Allow-Origin</code> 헤더 설정을 통해 해당 에러를 해결했다.</li>
</ul>
<h3 id="태그-24">태그</h3>
<p>Web, CORS</p>
<br>

<p>A.
브라우저의 보안 정책입니다.
브라우저에 랜더링 된 특정 페이지는 자신의 출처의 리소스만 불러올 수 있습니다.
다른 출처의 리소스를 불러오려면 해당 출처에서 올바른 CORS 헤더를 포함한 응답을 반환해야 합니다.</p>
<h3 id="simple-request">Simple Request</h3>
<p>브라우저는 서버에 본 요청을 일단 즉시 보냅니다.
응답 헤더에 CORS 허가 내용이 있다면 정상으로 받아들이고, 그렇지 않다면 브라우저에서 CORS 에러를 발생시킵니다.</p>
<p>이 방식은 본 요청이 무조건 서버에 가기 때문에, 보안상 취약합니다.</p>
<p>따라서 이 방식은 특정 조건을 만족하는 요청만 보낼 수 있습니다.</p>
<h3 id="preflighted-request">Preflighted Request</h3>
<p>Simple Request에 해당하지 않는 요청은 브라우저가 Preflighted Request 요청을 본 요청보다 먼저 서버에 보내봅니다. OPTIONS 메소드 요청을 통해 이런 요청을 할 건데, CORS허가가 되는지 서버에 미리 물어봅니다. 서버가 허가된다고 응답하면, 그 때 본 요청을 보냅니다.</p>
<p>허가된 CORS 요청만 보낼 수 있으므로, 보안상 더욱 유리합니다.</p>
<br>


<h2 id="docs-spring-rest-docs---5">[Docs] Spring REST Docs - 5</h2>
<h3 id="내용-25">내용</h3>
<ul>
<li>프론트 크루들과 협업하기 위해 API 문서화가 필요했다.</li>
<li>테스트를 통과해야 문서화가 되고, 이에 따라 프로덕션 코드가 실시간으로 반영되는 Spring REST Docs를 적용했다.<h3 id="태그-25">태그</h3>
Docs, REST</li>
</ul>
<br>

<p>A.
프로덕션 코드와 문서의 동기화를 가장 중요하게 생각해 Spring Rest Docs를 적용했습니다.</p>
<p>Swagger는 문서와 프로덕션 코드가 동기화 되지 않을 수 있다는 단점이 있습니다.</p>
<p>반면에 Spring Rest Docs는 테스트가 성공해야 문서화가 되기 때문에 프로덕션 코드와 문서가 항상 동기화됩니다.</p>
<p>이러한 이유로 저는 Spring Rest Docs를 선택했습니다.</p>
<br>

<h2 id="oop-다형성을-통한-if문-제거---5">[OOP] 다형성을 통한 if문 제거 - 5</h2>
<h3 id="내용-26">내용</h3>
<ul>
<li>지하철 요금 정책이 추가될 때 마다 if문이 생겼다.</li>
<li>Enum과 함수형 인터페이스의 다형성을 사용해 if문을 제거했다.</li>
</ul>
<h3 id="태그-26">태그</h3>
<p>OOP, Java</p>
<br>

<p>A.
요금 정책 각각을 Enum 상수로 정의했습니다.</p>
<p>각 Enum 상수에, 해당 정책의 조건을 나타내는 람다식과 정책 객체를 반환하는 함수를 함수형 인터페이스 타입의 인스턴스 변수로 지정했습니다.</p>
<p>이를 통해 정책마다 추가되는 if문을 제거할 수 있었습니다.</p>
<p>람다식이란 메서드를 하나의 식으로 표현한 것입니다.</p>
<p>람다식은 사실 메서드가 하나만 존재하는 익명 클래스의 객체입니다.</p>
<p>메서드가 하나만 존재하는 익명 클래스의 객체를 최대한 간소화시켜 식의 형태로 표현한 것이 람다식 입니다.</p>
<p>람다식을 사용하려면 이를 참조할 타입이 있어야 합니다.</p>
<p>람다식은 메서드가 하나만 존재하는 익명 클래스의 객체이기 때문에, 오직 하나의 추상메서드만 정의되어 있는 인터페이스 타입으로 참조하기로 했고, 이를 함수형 인터페이스라고 합니다.</p>
<p>static 메서드와 default 메서드의 개수에는 제약이 없습니다.</p>
<br>
]]></description>
        </item>
        <item>
            <title><![CDATA[우테코 Lv2 지하철 미션 1단계 회고]]></title>
            <link>https://velog.io/@taehee-kim-dev/%EC%9A%B0%ED%85%8C%EC%BD%94-Lv2-%EC%A7%80%ED%95%98%EC%B2%A0-%EB%AF%B8%EC%85%98-1%EB%8B%A8%EA%B3%84-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@taehee-kim-dev/%EC%9A%B0%ED%85%8C%EC%BD%94-Lv2-%EC%A7%80%ED%95%98%EC%B2%A0-%EB%AF%B8%EC%85%98-1%EB%8B%A8%EA%B3%84-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Thu, 20 May 2021 11:32:08 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>Hostname/IP 안 맞는 문제</p>
<ul>
<li>우선 필요한 dependency를 설치하지 않았었다.</li>
<li>www. DNS설정을 해버리면, TLS 인증서를 발급받았을 때 유효한 도메인 이름과 다르다고 인식해 허가를 내주지 않는다.</li>
<li>docker가 자꾸 -1 코드를 내며 죽는 문제에서는, nginx.conf 내에 event {} 라인을 추가해주면 된다(CU가 이미 써줬었다).</li>
<li>upstream app server에서 localhost는 127.0.0.1이 아닌, 172.18.0.1로 설정해주어야한다. 이는 docker를 통해 proxy를 띄우면서 설정이 변경되는 것으로 보인다.</li>
</ul>
</li>
<li><p>DATABASE문제</p>
<ul>
<li>소문자와 대문자를 구분하기 시작했다.</li>
<li>DataLoader는 H2를 기준으로 만들어져 있다. Mysql로 바꾸고 나서도 서버가 실행될 때마다   - 이미 있는 Data를 load하려고 하다보니 SQL 문법 에러가 났다. -&gt; Profile 설정을 통해 해결</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[우아한테크코스 3기 백엔드 Lv2 [배포/운영 인프라] 학습로그]]></title>
            <link>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv2-%EB%B0%B0%ED%8F%AC%EC%9A%B4%EC%98%81-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8</link>
            <guid>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv2-%EB%B0%B0%ED%8F%AC%EC%9A%B4%EC%98%81-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8</guid>
            <pubDate>Mon, 17 May 2021 04:54:55 GMT</pubDate>
            <description><![CDATA[<h2 id="운영-bastion-서버-적용---3">[운영] Bastion 서버 적용 - 3</h2>
<h3 id="내용">내용</h3>
<ul>
<li>일반 서비스 접속용 경로와 별도로 관리자용 접속 경로인 Bastion 서버를 구성함.</li>
<li>서비스 접속 경로의 책임과 관리자 접속 경로의 책임을 분리함.</li>
<li>Bastion 서버가 피해를 보더라도 이 부분만 재구성하면 되므로, 서비스에 영향을 최소화 할 수 있음.</li>
<li>DDos 공격을 받고 있다면 일반 서비스 접속용 경로로 접속할 수 없기 때문에, 관리자는 Bastion 서버를 통해 접속해야 함.</li>
</ul>
<h3 id="태그">태그</h3>
<p>운영, 보안</p>
<br>

<h2 id="운영-thread-dump-모니터링---2">[운영] Thread Dump 모니터링 - 2</h2>
<h3 id="내용-1">내용</h3>
<ul>
<li>Thread들의 실시간 상태를 모니터링 할 수 있다.</li>
<li>에러가 발생했을 경우, 디버깅에 유리하다.</li>
</ul>
<h3 id="태그-1">태그</h3>
<p>운영, Thread</p>
<br>

<h2 id="log-logback-적용---4">[Log] LogBack 적용 - 4</h2>
<h3 id="내용-2">내용</h3>
<ul>
<li>Logger의 이름별로 다른 파일에 로그가 쌓이도록 해, 로그 관리가 용이해짐.</li>
</ul>
<h3 id="태그-2">태그</h3>
<p>Log</p>
<br>

<h2 id="aws-cloudwatch---4">[AWS] CloudWatch - 4</h2>
<h3 id="내용-3">내용</h3>
<ul>
<li>EC2의 네트워크 상태, CPU, 메모리/디스크 사용률 등을 CloudWatch 대시보드에서 시각적으로 확인할 수 있음.</li>
<li>로그 파일들을 CloudWatch에 연동시켜 EC2에 접속하지 않고도 AWS 홈페이지에서 파일별 로그들을 편리하게 확인할 수 있음.</li>
</ul>
<h3 id="태그-3">태그</h3>
<p>운영, AWS, CloudWatch</p>
<h2 id="container-docker-적용---5">[Container] Docker 적용 - 5</h2>
<h3 id="내용-4">내용</h3>
<ul>
<li>MySQL을 Docker로 띄웠다.</li>
<li>Docker만 설치되어 있다면, 배포 환경과 완전히 독립적으로 Container를 실행시킬 수 있었다.</li>
</ul>
<h3 id="태그-4">태그</h3>
<p>배포, Container, Docker</p>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[우아한테크코스 3기 백엔드 Lv2 [지하철 미션] 학습로그]]></title>
            <link>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv2-%EC%A7%80%ED%95%98%EC%B2%A0-%EB%AF%B8%EC%85%98-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8</link>
            <guid>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv2-%EC%A7%80%ED%95%98%EC%B2%A0-%EB%AF%B8%EC%85%98-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8</guid>
            <pubDate>Sun, 16 May 2021 07:11:19 GMT</pubDate>
            <description><![CDATA[<h2 id="test-atdd---5">[Test] ATDD - 5</h2>
<h3 id="내용">내용</h3>
<ul>
<li>API 스펙 요구사항에 대해 ATDD를 진행했다.</li>
<li>테스트가 모두 통과할 때 마다, API 스펙 요구사항을 충족하고 있음을 확신할 수 있었다.</li>
<li>테스트가 추상적이어서, 리팩토링에 따른 테스트코드 수정이 거의 불필요했다.</li>
</ul>
<h3 id="태그">태그</h3>
<p>Test, ATDD</p>
<br>

<h2 id="인증-jwt---5">[인증] JWT - 5</h2>
<h3 id="내용-1">내용</h3>
<ul>
<li>로그인 인증에 JWT를 사용했다.</li>
<li>상태 정보(세션 등)를 서버에 저장하지 않아도 돼, 서버 부담을 줄일 수 있었다.</li>
</ul>
<h3 id="태그-1">태그</h3>
<p>인증, JWT</p>
<br>

<h2 id="spring-interceptor---3">[Spring] Interceptor - 3</h2>
<h3 id="내용-2">내용</h3>
<ul>
<li>로그인 인증이 필요한 URI 요청에 Interceptor를 걸고, JWT 유효성 검사 로직을 추가했다.</li>
<li>JWT 검사 책임을 Interceptor로 분리할 수 있었다.</li>
</ul>
<h3 id="태그-2">태그</h3>
<p>Spring, Interceptor</p>
<br>

<h2 id="spring-custom-annotation-argumentresolver---3">[Spring] Custom Annotation, ArgumentResolver - 3</h2>
<h3 id="내용-3">내용</h3>
<ul>
<li>Custom Annotation을 ArgumentResolver에 등록해, 해당 Annotation이 메소드의 매개변수 앞에 붙어있다면 매개변수에 현재 로그인 되어있는 Member객체를 넣어주도록 했다.</li>
<li>로그인 사용자 사전 조회 책임을 <code>Custom Annotation + ArgumentResolver</code>로 분리할 수 있었다.</li>
</ul>
<h3 id="태그-3">태그</h3>
<p>Spring, Annotation, ArgumentResolver</p>
<br>

<h2 id="oop-layer와-domain의-책임-분리---4">[OOP] Layer와 Domain의 책임 분리 - 4</h2>
<h3 id="내용-4">내용</h3>
<ul>
<li>Service 레이어에 모든 비즈니스 로직을 작성하다보니 Service 클래스가 매우 커지고, 도메인과 Service 레이어의 책임 분할이 되지 않았다.</li>
<li>비즈니스 로직은 모두 도메인 내부로 옮기고, Service 레이어에서는 도메인의 비즈니스 로직 실행 순서만 보장하도록 해, 각각의 책임을 분리했다.</li>
</ul>
<h3 id="태그-4">태그</h3>
<p>OOP, Domain, Service</p>
<br>

<h2 id="jdbc-namedparameterjdbctemplate-적용---3">[JDBC] NamedParameterJDBCTemplate 적용 - 3</h2>
<h3 id="내용-5">내용</h3>
<ul>
<li>파라미터를 순서 숫자가 아닌 컬럼명 문자열로 표기해 더욱 직관적이다. 유지보수에 유리하다.</li>
</ul>
<h3 id="태그-5">태그</h3>
<p>JDBC</p>
<br>

<h2 id="암호화-bcrypt---5">[암호화] BCrypt - 5</h2>
<h3 id="내용-6">내용</h3>
<ul>
<li>리뷰어 미립이 Hash 암호화에 대한 학습 키워드를 제공해줬다.</li>
<li>같은 원본 데이터에 대해 암호화를 할 때마다 매번 다른 Hash 결과값이 나오는 BCrypt에 대해 학습했다.</li>
</ul>
<h3 id="태그-6">태그</h3>
<p>암호화</p>
<br>

<h2 id="db-transaction-격리-수준---5">[DB] Transaction 격리 수준 - 5</h2>
<h3 id="내용-7">내용</h3>
<ul>
<li>Transaction 격리 수준에 대해 공부하고, 지하철 구간 수정 작업 단위에는 얼만큼의 격리 수준으로 Transaction을 걸어야 하는지 리뷰어 재연링과 함께 토론하며 고민했다.</li>
</ul>
<h3 id="태그-7">태그</h3>
<p>DB, Transaction</p>
<br>

<h2 id="oop-원시값-객체-포장---2">[OOP] 원시값 객체 포장 - 2</h2>
<h3 id="내용-8">내용</h3>
<ul>
<li>지하철 요금 원시값을 <code>Fare</code> 값 객체로 포장했다.</li>
<li>생성자에서 값 자체의 유효성 검증을 할 수 있었다.</li>
<li>비즈니스 로직을 객체 내부에서 처리할 수 있었다.</li>
</ul>
<h3 id="태그-8">태그</h3>
<p>OOP</p>
<br>

<h2 id="배포-배포-자동화---4">[배포] 배포 자동화 - 4</h2>
<h3 id="내용-9">내용</h3>
<ul>
<li>배포 자동화가 적용되지 않은 상태에서는 <code>github에서 최신 코드 가져오기 -&gt; 빌드 -&gt; 기존에 실행중인 애플리케이션 종료 -&gt; 새로운 버전의 애플리케이션 실행</code> 의 과정을 일일이 명령어를 쳐서 실행해야 했다.</li>
<li>이 과정 전체를 shell 스크립트로 작성했다.</li>
<li>해당 shell 스크립트를 실행시키기만 하면 새로운 버전의 애플리케이션이 자동으로 배포되어 배포가 매우 편리해졌다.</li>
</ul>
<h3 id="태그-9">태그</h3>
<p>배포</p>
<br>

<h2 id="web-cors-정책---3">[Web] CORS 정책 - 3</h2>
<h3 id="내용-10">내용</h3>
<ul>
<li>프론트 서버와 백엔드 서버가 따로 운영되면서, <code>CORS</code> 정책 위반 에러가 발생했다.</li>
<li><code>Access-Control-Allow-Origin</code> 헤더 설정을 통해 해당 에러를 해결했다.</li>
</ul>
<h3 id="태그-10">태그</h3>
<p>Web, CORS</p>
<br>


<h2 id="docs-spring-rest-docs---5">[Docs] Spring REST Docs - 5</h2>
<h3 id="내용-11">내용</h3>
<ul>
<li>프론트 크루들과 협업하기 위해 API 문서화가 필요했다.</li>
<li>테스트를 통과해야 문서화가 되고, 이에 따라 프로덕션 코드가 실시간으로 반영되는 Spring REST Docs를 적용했다.<h3 id="태그-11">태그</h3>
Docs, REST</li>
</ul>
<br>

<h2 id="oop-다형성을-통한-if문-제거---5">[OOP] 다형성을 통한 if문 제거 - 5</h2>
<h3 id="내용-12">내용</h3>
<ul>
<li>지하철 요금 정책이 추가될 때 마다 if문이 생겼다.</li>
<li>Enum과 함수형 인터페이스의 다형성을 사용해 if문을 제거했다.</li>
</ul>
<h3 id="태그-12">태그</h3>
<p>OOP, Java</p>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[Java Enum]]></title>
            <link>https://velog.io/@taehee-kim-dev/Java-Enum</link>
            <guid>https://velog.io/@taehee-kim-dev/Java-Enum</guid>
            <pubDate>Wed, 21 Apr 2021 05:34:51 GMT</pubDate>
            <description><![CDATA[<p>Java에서 특정 값들을 모아 하나의 타입으로 선언할 수 있습니다. 이 타입을 <strong>Enum 타입</strong> 이라고 합니다. </p>
<p>예를 들어 한 주의 요일들은 <strong>(월, 화, 수, 목, 금, 토, 일)</strong> 이 있습니다.
이 요일들을 묶어 Week라는 하나의 Enum타입으로 선언할 수 있습니다.</p>
<p>그리고 Week Enum 타입 내의 값들 <strong>(월, 화, 수, 목, 금, 토, 일)</strong> 을 <strong>Enum 상수</strong> 라고 합니다.</p>
<p>각 Enum 상수에 해당되는 Week Enum 객체가 Heap 영역에 생성됩니다.
각 Enum 상수들은 자신의 Week Enum 객체를 Method 영역에서 참조합니다.</p>
<p>Enum 타입의 참조변수로 특정 Week Enum 객체를 참조할 수 있습니다.</p>
<p>예를 들어, 다음의 경우를 보겠습니다.</p>
<pre><code>Week today = Week.MONDAY;</code></pre><p>참조 변수 <code>today</code>는 Stack 영역에 저장됩니다.
그리고 이 <code>today</code>는 Method 영역에 있는 <code>MONDAY 객체</code>의 주소값을 그대로 참조합니다.
즉, <code>today</code>와 <code>Week.MONDAY</code>는 Heap 영역에 있는 같은 <code>Week Enum 타입</code>의 <code>MONDAY 객체</code>를 참조합니다.</p>
<p>따라서, 아래의 테스트 코드는 통과합니다.</p>
<pre><code>@DisplayName(&quot;Enum 객체는 각 Enum 상수별로 각각 하나씩 생성되고, 참조된다.&quot;)
@Test
void enumTypeTest() {
    Week thisWeekMonday = Week.MONDAY;
    Week nextWeekMonday = Week.MONDAY;

    assertThat(thisWeekMonday).isSameAs(nextWeekMonday);
}</code></pre><br>

<blockquote>
</blockquote>
<p>참조 : <a href="https://honbabzone.com/java/java-enum/#step-2-enum%EA%B3%BC-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0">Java Enum이란</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[우아한테크코스 3기 백엔드 Lv2 모의면접 대상 Lv1 학습로그 모음 - 답변]]></title>
            <link>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv2-%EB%AA%A8%EC%9D%98%EB%A9%B4%EC%A0%91-%EB%8C%80%EC%83%81-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8-%EB%AA%A8%EC%9D%8C-%EB%8B%B5%EB%B3%80</link>
            <guid>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv2-%EB%AA%A8%EC%9D%98%EB%A9%B4%EC%A0%91-%EB%8C%80%EC%83%81-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8-%EB%AA%A8%EC%9D%8C-%EB%8B%B5%EB%B3%80</guid>
            <pubDate>Tue, 20 Apr 2021 12:02:56 GMT</pubDate>
            <description><![CDATA[<h1 id="lv1">Lv1</h1>
<hr>
<h1 id="🚀-로또---1단계---자동">🚀 로또 - 1단계 - 자동</h1>
<hr>
<h2 id="설계-mvc---3">[설계] MVC - 3</h2>
<h3 id="내용">내용</h3>
<ul>
<li><p>핵심 비즈니스 로직을 가지는 도메인 객체들을 domain 패키지에, UI와 관련된 객체들을 view 패키지에 구현했다.</p>
</li>
<li><p>MVC 패턴 기반으로 view 패키지의 객체가 domain 패키지 객체에 의존할 수 있지만, 
domain 패키지의 객체는 view 패키지 객체에 의존하지 않도록 구현했다.</p>
</li>
</ul>
<br>

<p><strong>Q.</strong></p>
<blockquote>
<p><strong>&quot;view 패키지의 객체가 domain 패키지 객체에 의존할 수 있지만, 
domain 패키지의 객체는 view 패키지 객체에 의존하지 않도록 구현했다.&quot;</strong></p>
</blockquote>
<p><strong>라고 했는데, 왜 그렇게 했나요?</strong></p>
<p><strong>A. 미션 프로그래밍 요구사항이었습니다. view와 domain의 의존성을 완전히 분리하기 위해 중간에 Controller를 두고, DTO로 데이터를 주고 받습니다. 해당 미션을 진행할 때 아직 DTO에 대해 잘 몰라서 적용하지 못했습니다. 하지만 DTO에 대해 학습한 이후의 미션들에는 DTO를 사용해 view와 domain의 의존성을 완전히 분리했습니다.</strong></p>
<hr>
<br>

<h2 id="oop---일급-컬렉션-적용---2">[OOP] - 일급 컬렉션 적용 - 2</h2>
<h3 id="내용-1">내용</h3>
<ul>
<li>도메인을 담는 컬렉션들은 모두 일급 컬렉션으로 감싸서 최대한 캡슐화하고, 다양한 비즈니스 로직을 내부에 구현했다.</li>
<li>외부에서는 내부의 구현은 모른 채, 단순히 요청 메시지만 보내고 응답 메시지를 받는, OOP적인 구현을 했다.<h3 id="링크">링크</h3>
</li>
<li><a href="https://github.com/woowacourse/java-lotto/blob/5aaf6d2a9d737be166577878c369fb5cd9b6e1a0/src/main/java/lotto/domain/LottoTicket.java" target="_blank">LottoTicket 일급 컬렉션 적용</a></li>
<li><a href="https://github.com/woowacourse/java-lotto/blob/5aaf6d2a9d737be166577878c369fb5cd9b6e1a0/src/main/java/lotto/domain/ticketpurchase/PurchasedLottoTickets.java" target="_blank">PurchasedLottoTickets 일급 컬렉션 적용</a></li>
<li><a href="https://jojoldu.tistory.com/412" target="_blank">jojoldu님의 일급 컬렉션 (First Class Collection)의 소개와 써야할 이유</a></li>
<li><a href="https://woowacourse.github.io/javable/post/2020-05-08-First-Class-Collection/" target="_blank">javable의 일급 컬렉션을 사용하는 이유</a></li>
</ul>
<br>

<p><strong>Q. 일급 컬렉션에 대해 설명해 주세요.</strong></p>
<p><strong>A. 일급 컬렉션은 클래스에 필드가 하나만 있고, 해당 필드가 컬렉션을 참조하는 것을 말합니다.</strong></p>
<p>즉, 하나의 컬렉션을 클래스로 감싸는 것 인데요.</p>
<p>이렇게 하면 해당 컬렉션에 이름을 부여할 수 있습니다.</p>
<p>또한, 캡슐화가 적용되어 비즈니스 로직이 적용된 자료구조로 만들 수 있습니다.</p>
<p>일급 컬렉션 클래스에 컬렉션의 값을 변경할 수 있는 메소드가 없도록 하면, 불변 컬렉션으로 만들 수 있습니다.</p>
<p>요즘과 같이 소프트웨어 규모가 커지고 있는 상황에서 불변 객체는 아주 중요합니다.</p>
<p>각각의 객체들의 값이 바뀔일이 없다는게 보장되면, 그만큼 코드를 이해하고 수정하는데 사이드 이펙트가 최소화되기 때문입니다.</p>
<hr>
<br>

<h2 id="jdk---enum---4">[JDK] - Enum - 4</h2>
<h3 id="내용-2">내용</h3>
<ul>
<li>당첨 순위를 Enum으로 선언했다.</li>
<li>각 등수를 상수로 구분하고, 관련된 메시지나 당첨 상금들을 각각 묶으니 분리와 활용이 편리해졌다.</li>
<li>해당 Enum 클래스와 관련된 함수들 또한 클래스 내부에 위치시켜, OOP적으로 구현했다.<h3 id="링크-1">링크</h3>
</li>
<li><a href="https://github.com/woowacourse/java-lotto/blob/5aaf6d2a9d737be166577878c369fb5cd9b6e1a0/src/main/java/lotto/type/LottoMatchType.java" target="_blank">당첨 순위 Enum 타입 처리</a></li>
<li><a href="https://woowabros.github.io/tools/2017/07/10/java-enum-uses.html" target="_blank">우아한형제들 기술블로그 Java Enum 활용기</a></li>
<li><a href="https://honbabzone.com/java/java-enum/#step-2-enum%EA%B3%BC-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0">Java Enum이란</a><br>

</li>
</ul>
<p><strong>Q. Enum이 뭔가요? 어떤 방식으로 생성되고 사용되나요?</strong></p>
<p><strong>A. Java에서 특정 값들을 모아 하나의 타입으로 선언할 수 있습니다. 이 타입을 &quot;Enum 타입&quot; 이라고 합니다.</strong></p>
<p>예를 들어 한 주의 요일들의 값, MONDAY 부터 SUNDAY 까지를 하나의 Enum 타입으로 선언할 수 있습니다.</p>
<p>이때, 각 요일들의 값을 <strong>Enum 상수</strong> 라고 합니다.</p>
<p>각 Enum 상수에 해당되는 Week Enum 객체가 Heap 영역에 생성됩니다.
그리고 각 Enum 상수들은 자신의 Week Enum 객체를 Method 영역에서 참조합니다.</p>
<p>Enum 타입의 참조변수로 특정 Week Enum 객체를 참조할 수 있습니다.
참조 변수는 Stack 영역에 저장되고, Method 영역에 있는 MONDAY 객체의 주소값을 그대로 복사해서 참조합니다. 즉, 참조되는 모든 Week Enum 타입의 MONDAY 객체는 단 하나의 동일한 객체입니다.</p>
<hr>
<br>

<h1 id="🚀-로또---2단계---수동-구매">🚀 로또 - 2단계 - 수동 구매</h1>
<hr>
<h2 id="jdk-map-api---1">[JDK] Map API - 1</h2>
<h3 id="내용-3">내용</h3>
<ul>
<li>리뷰어 휴의 피드백을 받아, Map에서 get()으로 꺼내 값을 증가시킨 후 다시 put()으로 저장하도록 작성했던 코드를, Map API computeIfPresent() 를 사용해 더욱 명시적이고 우아하게 바꿨다.</li>
</ul>
<h3 id="링크-2">링크</h3>
<ul>
<li><a href="http://tech.javacafe.io/2018/12/03/HashMap/" target="_blank">자바 HashMap을 효과적으로 사용하는 방법</a></li>
<li><a href="https://github.com/woowacourse/java-lotto/blob/6ba6f49c152acc460a7c4516fee685d8d13b9419/src/main/java/lotto/domain/ticketresult/LottoResult.java" target="_blank">LottoResult.java 의 increaseOneCountOfLottoMatchType()</a></li>
</ul>
<br>

<p><strong>Q. Map의 &quot;computeIfPresent()&quot;는 어떤 API인가요?</strong></p>
<p><strong>A. 첫 번째 인자로 key 값을 받습니다. 해당 key 값이 Map의 key 값으로 존재하면, 두 번째 인자로 전달된 람다 함수가 호출됩니다. 존재하지 않을경우에는 호출되지 않습니다.</strong></p>
<p>두 번째 인자로 전달된 람다 함수는 두 개의 매개변수 (key, value)를 가질 수 있습니다.</p>
<hr>
<br>

<h2 id="jdk-bigdecimal-api---3">[JDK] BigDecimal API - 3</h2>
<h3 id="내용-4">내용</h3>
<ul>
<li><p>수익률 소수점 계산에서 <strong>BigDecimal API</strong> 사용이 필요했다.</p>
</li>
<li><p>사용법을 제대로 숙지하지 않은 채 사용해, 소수점 셋 째 자리에서 반올림이 되지 않는 버그가 발생했다.</p>
</li>
<li><p><strong>BigDecimal API 문서</strong> 와 구글링을 참고하고, <strong>학습 테스트</strong> 를 통해 정확한 API 사용법을 익혀 버그를 고쳤다. </p>
</li>
<li><p>맨 처음 구현할 때, 구글링을 통해 찾은 <strong>잘못된 자료</strong> 를 참고해서 코드를 작성했다. 사용법이 애매할 때는 <strong>학습 테스트</strong> 로 제대로 익힌 뒤에 사용해야 겠다.</p>
</li>
</ul>
<h3 id="링크-3">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-lotto/blob/step2/src/main/java/lotto/domain/ticketresult/LottoResult.java" target="_blank">LottoResult.java</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-lotto/blob/step2/src/test/java/study/bigdecimal/BigDecimalTest.java" target="_blank">BigDecimalTest.java</a></li>
<li><a href="https://www.geeksforgeeks.org/bigdecimal-divide-method-in-java-with-examples/" target="_blank">BigDecimal divide() Method in Java with Examples</a></li>
<li><a href="https://stackoverflow.com/questions/10637232/how-can-i-divide-properly-using-bigdecimal" target="_blank">How can I divide properly using BigDecimal</a></li>
<li><a href="https://jsonobject.tistory.com/466" target="_blank">Java, BigDecimal 사용법 정리</a></li>
</ul>
<br>

<p><strong>Q. BigDecimal을 왜 사용했나요?</strong></p>
<p><strong>A. 로또 수익률 계산에서 double 타입을 사용했을 때, 부동소수점 방식 때문에 오차가 발생했습니다. 이 오차를 없애고자 BigDecimal을 사용했습니다.</strong></p>
<hr>
<br>

<h1 id="🚀-블랙잭---1단계">🚀 블랙잭 - 1단계</h1>
<hr>
<h2 id="디자인-패턴-전략-패턴---4">[디자인 패턴] 전략 패턴 - 4</h2>
<h3 id="내용-5">내용</h3>
<ul>
<li><p>딜러가 몇 장의 카드를 반환할지 결정하는 데에 전략패턴을 사용했다.</p>
</li>
<li><p>전략패턴을 적용하면 최소한의 코드 수정으로 전략을 다르게 가져갈 수 있고, 확장에도 자유로움을 알 수 있었다.</p>
</li>
</ul>
<h3 id="링크-4">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/player/strategy/CardOpenStrategy.java">CardOpenStrategy.java</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/player/strategy/OneCardOpenStrategy.java">OneCardOpenStrategy.java</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/player/strategy/AllCardsOpenStrategy.java">AllCardsOpenStrategy.java</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/player/Dealer.java">Dealer.java</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/controller/BlackJackController.java">BlackJackController.java</a></li>
</ul>
<br>

<p><strong>Q. 전략 패턴이 무엇이고, 왜 사용했는지 설명해주세요.</strong></p>
<p><strong>A. 전략 패턴은, 외부에서 전략 객체를 주입해 전략에 따라 객체의 로직을 다르게 가져갈 수 있는 설계 방식입니다.</strong></p>
<p>전략들을 하나의 인터페이스로 추상화 합니다. 그리고 이 인터페이스를 구현하는 각 전략 구현체들은 각자의 전략에 맞도록 함수를 구현합니다.</p>
<p>전략을 사용하는 객체는 전략 세팅 메소드를 통해 외부로부터 전략들을 주입받습니다.
인터페이스의 다형성을 통해 전략 세팅 메소드의 매개변수로 다양한 전략 구현체들을 받을 수 있습니다.</p>
<hr>
<br>

<h2 id="디자인-패턴-싱글톤-패턴---2">[디자인 패턴] 싱글톤 패턴 - 2</h2>
<h3 id="내용-6">내용</h3>
<ul>
<li><p>페어 멍토의 제안으로, 애플리케이션 전체에서 공유되는 카드 뭉치 객체에 싱글톤 패턴을 적용했다.</p>
</li>
<li><p>애플리케이션 전체에서 공유되는 객체에 대해서는 static 적용만 해왔는데, 싱글톤 패턴 적용에 대해 새롭게 배웠다.</p>
</li>
</ul>
<h3 id="링크-5">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/card/Cards.java" target="_blank">Cards.java</a></li>
</ul>
<br>

<p><strong>Q. 싱글톤 패턴이 무엇이고, 왜 사용했는지 설명해주세요.</strong></p>
<p><strong>A. 객체의 인스턴스를 단 하나만 생성하는 패턴을 싱글톤 패턴이라고 합니다. 블랙잭 미션의 애플리케이션은 한 번 실행 할 때 마다 한 번의 게임만 진행합니다. 따라서 애플리케이션에서 단 하나의 카드 뭉치 객체만 필요해 싱글톤 패턴을 적용했습니다.</strong></p>
<p>싱글턴 패턴은 멀티쓰레드 환경의 동시성 문제를 고려해서 설계해야 합니다.</p>
<p>이를 고려하지 않으면, 싱글톤의 목적과 달리 두 개의 인스턴스가 생성될 수도 있습니다.</p>
<p>가장 많이 사용되는 방식은 <strong>LazyHoder 방식</strong> 입니다.</p>
<p><code>volatile</code> 이나 <code>synchronized</code> 키워드 없이도 동시성 문제를 해결하기 때문에 성능이 뛰어납니다.</p>
<p>특정 메서드를 호출할 때, Inner static 클래스의 클래스 인스턴스가 초기화 됩니다.</p>
<p>별도의 키워드 없이 동시성 문제와 사전 초기화로 인한 메모리 낭비 문제를 모두 해결할 수 있어, 가장 많이 사용됩니다.</p>
<hr>
<br>

<h2 id="java-정적-팩토리-메서드---3">[Java] 정적 팩토리 메서드 - 3</h2>
<h3 id="내용-7">내용</h3>
<ul>
<li>new 연산자는 단순히 생성밖에 못한다.</li>
<li>정적 팩토리 메서드를 이용하면, 호출하는 메서드에 따라 로직을 적용해 다른 생성 결과를 낼 수 있다.</li>
</ul>
<h3 id="링크-6">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/card/Cards.java">Cards.java</a></li>
</ul>
<br>

<p><strong>Q. 정적 팩토리 메서드가 무엇이고, 왜 사용했는지 설명해주세요.</strong></p>
<p><strong>A. 정적 팩토리 메서드는 객체 생성 역할을 하는 static 메서드 입니다. new 키워드는 단순한 객체 생성만 할 수 있습니다. 하지만 정적 팩토리 메서드는 객체 생성 로직에 이름을 부여할 수 있고, 객체 생성에 특정 로직을 적용할 수 있습니다. 캐싱을 했을 경우, 캐싱되어있는 객체를 반환할 수 있습니다. 메서드 내부 로직을 통해 하위 자료형 객체를 반환할 수 있습니다.</strong></p>
<p>단점은 정적 팩토리 메서드의 구체적인 내부 구현 방식을 모르는 사람이 사용할 때, 어떤 로직이 적용되었는지를 파악하기 위해 메서드 내부를 확인해야 한다는 점 입니다.</p>
<hr>
<br>

<h1 id="lv2">Lv2</h1>
<hr>
<h1 id="🚀-spring-체스---3단계-추가미션">🚀 Spring 체스 - 3단계, 추가미션</h1>
<hr>
<h2 id="spring-mvc-restcontrolleradvice---3">[Spring MVC] @(Rest)ControllerAdvice - 3</h2>
<h3 id="내용-8">내용</h3>
<ul>
<li>Controller와 Exception의 범위를 지정하면, Exception을 알아서 잡아준다.</li>
<li>try ~ catch 문을 없앨 수 있어, 비즈니스 로직이 매우 깔끔해진다.</li>
</ul>
<h3 id="태그">태그</h3>
<p><strong>#Spring, #MVC, #Exception</strong></p>
<br>

<p><strong>Q. @ControllerAdvice 어노테이션이 무엇이고, @RestControllerAdvice 어노테이션과의 차이는 뭔가요?</strong></p>
<p><strong>A. @ControllerAdvice는 많은 Controller에서 발생하는 예외를 한 번에 잡아 처리해주는 어노테이션 입니다.</strong></p>
<p><strong>@ExceptionHandler</strong> 와 함께 쓰입니다.</p>
<p><strong>@ControllerAdvice</strong> 에서 적용할 Controller의 범위를 지정할 수 있고, <strong>@ExceptionHandler</strong> 에서 잡을 예외를 지정할 수 있습니다.</p>
<p>별도의 지정 없이 사용하면, 모든 컨트롤러에서 발생하는 예외를 처리하게 됩니다.</p>
<p><strong>@RestControllerAdvice</strong> 는 <strong>@ControllerAdvice</strong> 와 동일한 역할을 합니다. 차이점은, <strong>@ControllerAdvice</strong> 에 <strong>@ResponseBody</strong> 가 추가된 어노테이션이라는 점 입니다.</p>
<hr>
<br>

<h2 id="http-rest-api---2">[HTTP] REST API - 2</h2>
<h3 id="내용-9">내용</h3>
<ul>
<li>삭제 요청을 POST &quot;/delete&quot; 로 했었다.</li>
<li>DELETE &quot;games/${gameId}&quot; 로 바꾸니, API가 더욱 명시적으로 됐다.</li>
</ul>
<h3 id="태그-1">태그</h3>
<p><strong>#HTTP, #REST, #API</strong></p>
<br>

<p><strong>Q. REST, REST API, RESTful이 각각 무엇이고, 차이점은 뭔가요?</strong></p>
<p><strong>A. REST는 HTTP URI(Uniform Resource Identifier)를 통해 자원(Resource)을 명시하고, HTTP Method(POST, GET, PUT, DELETE)를 통해 해당 자원에 대한 CRUD Operation을 적용하는 것을 의미합니다. REST API는 REST를 기반으로 만들어진 API 입니다. RESTful은 REST API를 제공하는 웹 서비스를 지칭하는 말 입니다.</strong></p>
<p>REST는 표준이 아니다.
RESTful한 API를 구현하는 근본적인 목적은 일관적인 컨벤션을 통한 API의 이해도 및 호환성을 높이는 것이다.</p>
<h3 id="rest가-필요한-이유">REST가 필요한 이유</h3>
<ul>
<li>‘애플리케이션 분리 및 통합’</li>
<li>‘다양한 클라이언트의 등장’</li>
<li>최근의 서버 프로그램은 다양한 브라우저와 안드로이폰, 아이폰과 같은 모바일 디바이스에서도 통신을 할 수 있어야 한다.</li>
<li>이러한 멀티 플랫폼에 대한 지원을 위해 서비스 자원에 대한 아키텍처를 세우고 이용하는 방법을 모색한 결과, REST에 관심을 가지게 되었다.</li>
</ul>
<blockquote>
</blockquote>
<p>참조 : <a href="https://gmlwjd9405.github.io/2018/09/21/rest-and-restful.html">https://gmlwjd9405.github.io/2018/09/21/rest-and-restful.html</a></p>
<hr>
<br>

<h2 id="http-cookie---4">[HTTP] Cookie - 4</h2>
<h3 id="내용-10">내용</h3>
<ul>
<li>클라이언트에 값을 저장하고, 이후 요청마다 해당 값을 포함시키는데 적합한 쿠키를 사용했다.</li>
<li>한 번 쿠키를 발급해주면, 만료되기 전 까지 매 요청에 쿠키를 포함해 보내기 때문에 인증에 유용하다.</li>
</ul>
<h3 id="태그-2">태그</h3>
<p><strong>#HTTP, #Cookie</strong></p>
<br>

<p><strong>Q. 쿠키의 구체적인 동작 방식과, 세션과의 차이점을 말씀해 주세요.</strong></p>
<p><strong>A. 쿠키는 클라이언트 브라우저에 키와 값의 형태로 저장되는 데이터 입니다. 서버에서 응답 헤더에 쿠키 값을 설정하여 발급할 수 있습니다. 그러면, 해당 쿠키가 만료되기 전 까지 이후 요청 헤더에 자동으로 쿠키를 담아 전송합니다.</strong></p>
<p>쿠키는 HTTP의 특징이자 약점인 connectionless, stateless의 약점을 보완하기 위해 사용됩니다.</p>
<p>세션은 쿠키를 기반하고 있습니다.
쿠키는 데이터를 클라이언트 브라우저에 저장하지만, 세션은 데이터를 서버에 저장하고, 쿠키에 세션 id를 담아 응답으로 보냅니다.</p>
<p>즉, 세션은 쿠키를 통해 세션id만 주고받습니다. 서버는 요청 쿠키에 있는 세션id로 서버 내에 저장되어있는 데이터를 찾아 비즈니스 로직을 처리합니다. 쿠키는 데이터를 브라우저에 저장하지만, 세션은 데이터를 서버 내에 저장하기 때문에 보안 면에서 세션이 쿠키보다 우수합니다. 하지만, 사용자가 많아질수록 서버 메모리를 많이 차지한다는 단점이 있습니다.</p>
<p>세션은 브라우저를 종료하면 만료기간에 상관없이 바로 삭제되지만, 쿠키는 브라우저를 종료하더라도 만료기간이 남아있으면 유지됩니다.</p>
<hr>
<br>

<h2 id="보안-hash-암호화---4">[보안] Hash 암호화 - 4</h2>
<h3 id="내용-11">내용</h3>
<ul>
<li>프로그래밍 요구사항에서 비밀번호를 클라이언트에 저장하고, 매 요청에 같이 보내라는 부분이 있었다.</li>
<li>이 방식은 보안에 매우 취약하기 때문에, Hash 암호화를 사용했다.</li>
<li>쿠키나 요청 데이터가 탈취당하더라도 Hash 함수로 암호화 되어있기 때문에, 암호 본문은 알 수 없어 보안에 유리하다.</li>
</ul>
<h3 id="태그-3">태그</h3>
<p><strong>#보안, #Hash 암호화</strong></p>
<br>

<p><strong>Q. 암호화 방식들 중에 Hash 암호화를 사용한 이유가 뭔가요? 암호화의 방식과 장단점에 대해 말해주세요.</strong></p>
<p><strong>A. Hash는 단방향 암호화 기법입니다. 서비스에서 사용자 인증은 보안의 이유로 비밀번호 원문을 알 수 없도록 Hash 함수 같은 단방향 암호화 기법을 사용합니다.</strong></p>
<p>Hash 암호화 기법은 비밀번호 원문을 해시 알고리즘을 이용해 고정된 길이의 암호화된 문자열로 바꾸는 방식입니다. 이 특징을 사용해 <strong>&quot;인증&quot;</strong> 과 <strong>&quot;데이터 무결성 체크&quot;</strong> 에 많이 사용됩니다.</p>
<p>단방향 암호화 방식이기 때문에, 복호화 할 수 없습니다.</p>
<p>Hash 알고리즘은 원문이 같으면 Hash 값도 같습니다.</p>
<p>하지만 Hash 값의 길이 조건이 제한되어있다면, 이 제약사항 때문에 다른 원문에 같은 Hash값이 나올 수 있습니다. 이 Hash값 충돌이 적게 나타날 수록 좋은 Hash 함수 입니다.</p>
<p>사용 권장되고있는 <code>SHA-256</code> Hash 알고리즘을 사용했습니다.</p>
<p>서로 다른 원문이 <code>SHA-256</code> 알고리즘을 통해 같은 Hash값을 반환할 확률(Hash 충돌이 발생할 확률)은 
1초 뒤에 행성 충돌로 지구가 멸망할 확률보다 45배 적습니다. 그만큼 충돌 저항성이 높습니다. 이에 따라 보안이 중요한 수많은 곳(블록체인, 금융권 등)에서 사용하고 있어, 저도 사용했습니다.</p>
<hr>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[우아한테크코스 Lv1 한 달 생활기]]></title>
            <link>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-%ED%95%9C-%EB%8B%AC-%EC%83%9D%ED%99%9C%EA%B8%B0</link>
            <guid>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-%ED%95%9C-%EB%8B%AC-%EC%83%9D%ED%99%9C%EA%B8%B0</guid>
            <pubDate>Tue, 20 Apr 2021 10:25:59 GMT</pubDate>
            <description><![CDATA[<p>간절히 바라던 우테코가 시작됐다. 온라인 OT였지만, 그것만으로도 너무 설레서 떨릴 정도였다. 우아한형제들 전사교육팀의 에너지는 엄청났다. 젊음의 생기와 자유로움으로 가득한 이상적인 문화를 보여줬다. 어색한 기운이 종종 맴돌았지만, 새로운 시작의 느낌이기에 즐거웠다.</p>
<p>보이는 라디오. 아이스 브레이킹과 협업 능력 증진을 위한 미션이다. 팀원들을 온라인으로 처음 만나니 어색해, 진행이 더뎠다. 학교 조별과제가 떠오르며 &#39;내가 대본을 다 짜야 하나?&#39; 고민했지만, 우테코 크루들은 달랐다. 단 한 명도 수동적이지 않았고, 같이 만들어 나가려는 의욕이 강했다. 처음에 생각했던 것보다 훨씬 재미있는 시나리오를 함께 완성했다. 각자의 아이디어로 독특한 가상 배경, 유행어, 개그요소의 대사를 적용했다. 협업의 효율성과 즐거움을 모두 느낀 미션이었다.</p>
<p>온라인으로 시작한 페어프로그래밍 미션은 새로웠다. 지금까지 공부, 고민, 코딩 모든 것을 혼자 해 왔고, 이 방식이 진행 속도가 가장 빠르다고 생각했다. 코딩을 같이하면, 효율이 떨어질 것이라고 생각했다. 하지만 머지않아 큰 오해였음을 깨달았다. 두 개의 뇌가 동시에 하나의 문제를 해결한다. 의견 충돌이 일어나면, 최적의 합의점을 찾을 때까지 토론한다. 혼자 했을 때보다 훨씬 빨리, 좋은 코드를 작성할 수 있었다.</p>
<p>우테코는 스스로 성장하도록 돕는다. 미션 중심의 교육은, 주입식 교육과 정반대이다. 공부, 협업, 시간 관리까지 모든 것을 알아서 관리해야 한다. 이 방식은 정해진 기간 안에 동료들과 함께 높은 품질의 결과물을 완성하는 방법을 고민하게 한다. 또, 필요한 지식을 효율적으로 습득하는 방법을 찾게 한다. 매우 현명한 교육임을 몸소 느끼고 있다.</p>
<p>포비가 오프라인 출석 공지를 했다. 자취할 계획이었지만, 계속된 온라인 교육에 보류하고 있었다. 공지가 올라온 날부터, 오프라인 출석 날까지 며칠 남지 않아 마음이 급해졌다. 이틀 만에 방 계약을 하고 이사를 했다. 첫 자취여서 많이 혼란스럽고 낯설었다. 그래도 첫 오프라인 출석 날, 크루들을 직접 만나니 매우 반가웠다.</p>
<p>크루들과 같이 하는 모든 순간이 즐겁다. 온라인과 오프라인을 자유롭게 넘나들며 함께 공부하고, 토론하고, 코딩한다. 한 달 동안 매우 가파르게 성장했다. 우테코는 코딩이 메인인 줄 알았다. 하지만 소프트 스킬이 반 이상 차지한다는 것을 알게 되었다. 코딩만 잘하는 개발자보다 같이 협업하고 싶은 개발자가 되고 싶다. 그런 개발자는 유능할 뿐만 아니라 행복할 것이다. 우테코는 그런 개발자를 양성하는 곳이다.</p>
<p>한 달 만에 이 만큼 성장했는데, 수료할 때쯤엔 얼마나 더 성장해 있을까? 벌써 기대된다. 우테코 크루임에 감사하고, 행복하다. 이런 교육과정을 만들어 준 포비와 코치분들에게 감사하다. 내가 온전히 공부에 집중할 수 있도록 아낌없이 지원해 주고 계신 부모님께 감사하다. 세상의 발전에 기여해, 모두에게 꼭 보답할 것이다.</p>
<p>헬스가 취미다. 우테코에 집중하면서 운동을 못 했다. 자취방 주변이 낯설어서 헬스장을 등록할 여유가 없었다. 시간 대부분을 공부, 협업에 쏟다 보니 체력이 달리는 듯하다. 아무리 자도 피곤하고, 특히 일어날 때 굉장히 힘들다. 특강에서 선배들이 체력관리를 위해 운동을 꼭 하라고 했다. 최상의 컨디션 유지를 위해, 헬스장을 알아보려 한다. 인생 전체의 관점에서 길게 보자. 무작정 달려가면 지쳐 쓰러진다. 페이스를 잘 유지하자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[우아한테크코스 3기 백엔드 Lv2 모의면접 대상 학습로그 모음]]></title>
            <link>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv1-Lv2-%EC%B2%B4%EC%8A%A4-3%EB%8B%A8%EA%B3%84-%EC%B6%94%EA%B0%80%EB%AF%B8%EC%85%98-%EC%B5%9C%EC%B4%88-PR%EA%B9%8C%EC%A7%80%EC%9D%98-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv1-Lv2-%EC%B2%B4%EC%8A%A4-3%EB%8B%A8%EA%B3%84-%EC%B6%94%EA%B0%80%EB%AF%B8%EC%85%98-%EC%B5%9C%EC%B4%88-PR%EA%B9%8C%EC%A7%80%EC%9D%98-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 20 Apr 2021 03:52:33 GMT</pubDate>
            <description><![CDATA[<h1 id="lv1">Lv1</h1>
<hr>
<h1 id="🚀-로또---1단계---자동">🚀 로또 - 1단계 - 자동</h1>
<hr>
<h2 id="설계-클래스-다이어그램---5">[설계] 클래스 다이어그램 - 5</h2>
<h3 id="내용">내용</h3>
<ul>
<li><p>맨 처음 설계 과정에서 페어와 함께 클래스다이어그램을 그렸다. 
모든 세부사항, 구체적인 연관관계를 그렸다.
하지만 UML 정식 형식과 많이 달랐고, 너무 복잡해 이해하기 힘들었다.</p>
</li>
<li><p>페어프로그래밍을 종료하고, <strong>[UML 실전에서는 이것만 쓴다 / 로버트 C.마틴]</strong> 책을 보았다. 
그런데 오히려 로버트 마틴은 <strong>&quot;세세하게 이것저것 다 적어놔봤자 알아보기만 힘들고, 
구체적인것은 코드 구현시점에 정하면 된다. 모두가 잘 이해할 수 있도록 간소화 된 형식으로 UML을 작성해야 한다.&quot;</strong> 라고 했다. 
점선, 집합, 합성도 구분하지 말고, 단순히 관계만 알아보기 쉽게 표현하라고 했다. </p>
</li>
<li><p>로버트 마틴의 말을 참고해 최대한 이해하기 쉽도록 다시 UML을 그렸다. </p>
</li>
</ul>
<h3 id="링크">링크</h3>
<ul>
<li><a href="https://drive.google.com/file/d/1WDzeRYf3-g120NugP9Qva0rP8TvUbVx7/view?usp=sharing" target="_blank">페어와 함께 작성한, 상세하고 복잡한 첫 번째 클래스 다이어그램</a></li>
<li><a href="https://drive.google.com/file/d/1Wh35NfypyENLsiDnOUr5HSi8oWNV2pO-/view?usp=sharing" target="_blank">페어 프로그래밍 이후 혼자서 작성한, 이해에 중점을 둔 간결한 두 번째 클래스 다이어그램</a></li>
<li><a href="https://gmlwjd9405.github.io/2018/07/04/class-diagram.html" target="_blank">UML 클래스 다이어그램 작성법 1</a></li>
<li><a href="http://pages.cs.wisc.edu/~hasti/cs302/examples/UMLdiagram.html" target="_blank">UML 클래스 다이어그램 작성법 2</a></li>
</ul>
<br>

<h2 id="설계-mvc---3">[설계] MVC - 3</h2>
<h3 id="내용-1">내용</h3>
<ul>
<li><p>핵심 비즈니스 로직을 가지는 도메인 객체들을 domain 패키지에, UI와 관련된 객체들을 view 패키지에 구현했다.</p>
</li>
<li><p>MVC 패턴 기반으로 view 패키지의 객체가 domain 패키지 객체에 의존할 수 있지만, 
domain 패키지의 객체는 view 패키지 객체에 의존하지 않도록 구현했다.</p>
</li>
</ul>
<br>

<h2 id="tdd-테스트-주도-개발---4">[TDD] 테스트 주도 개발 - 4</h2>
<h3 id="내용-2">내용</h3>
<ul>
<li>모든 코드작성을 페어와 함께 TDD로 구현하였다.</li>
<li>TDD로 진행하니 구현된 코드의 기능에 확신을 가질 수 있었다.</li>
<li>리팩토링시, 테스트코드의 위력을 느낄 수 있었다. 자유롭고 확신있는 수정이 가능했다.<h3 id="링크-1">링크</h3>
</li>
<li><a href="https://github.com/taehee-kim-dev/java-lotto/tree/step1/src/test/java/lotto" target="_blank">테스트 코드들</a></li>
</ul>
<br>

<h2 id="oop---원시값-포장---2">[OOP] - 원시값 포장 - 2</h2>
<h3 id="내용-3">내용</h3>
<ul>
<li>로또 번호 등 모든 원시값을 포장했다.</li>
<li>포장된 원시값이 하나의 도메인이 됐다.</li>
<li>생성자에서 유효성 검사가 가능해져, 코드가 깔끔해졌다.<h3 id="링크-2">링크</h3>
</li>
<li><a href="https://github.com/woowacourse/java-lotto/blob/5aaf6d2a9d737be166577878c369fb5cd9b6e1a0/src/main/java/lotto/domain/LottoNumber.java" target="_blank">LottoNumber 원시값 포장</a></li>
</ul>
<br>

<h2 id="oop---일급-컬렉션-적용---2">[OOP] - 일급 컬렉션 적용 - 2</h2>
<h3 id="내용-4">내용</h3>
<ul>
<li>도메인을 담는 컬렉션들은 모두 일급 컬렉션으로 감싸서 최대한 캡슐화하고, 다양한 비즈니스 로직을 내부에 구현했다.</li>
<li>외부에서는 내부의 구현은 모른 채, 단순히 요청 메시지만 보내고 응답 메시지를 받는, OOP적인 구현을 했다.<h3 id="링크-3">링크</h3>
</li>
<li><a href="https://github.com/woowacourse/java-lotto/blob/5aaf6d2a9d737be166577878c369fb5cd9b6e1a0/src/main/java/lotto/domain/LottoTicket.java" target="_blank">LottoTicket 일급 컬렉션 적용</a></li>
<li><a href="https://github.com/woowacourse/java-lotto/blob/5aaf6d2a9d737be166577878c369fb5cd9b6e1a0/src/main/java/lotto/domain/ticketpurchase/PurchasedLottoTickets.java" target="_blank">PurchasedLottoTickets 일급 컬렉션 적용</a></li>
<li><a href="https://jojoldu.tistory.com/412" target="_blank">jojoldu님의 일급 컬렉션 (First Class Collection)의 소개와 써야할 이유</a></li>
<li><a href="https://woowacourse.github.io/javable/post/2020-05-08-First-Class-Collection/" target="_blank">javable의 일급 컬렉션을 사용하는 이유</a></li>
</ul>
<br>

<h2 id="jdk---enum---4">[JDK] - Enum - 4</h2>
<h3 id="내용-5">내용</h3>
<ul>
<li>당첨 순위를 Enum으로 선언했다.</li>
<li>각 등수를 상수로 구분하고, 관련된 메시지나 당첨 상금들을 각각 묶으니 분리와 활용이 편리해졌다.</li>
<li>해당 Enum 클래스와 관련된 함수들 또한 클래스 내부에 위치시켜, OOP적으로 구현했다.<h3 id="링크-4">링크</h3>
</li>
<li><a href="https://github.com/woowacourse/java-lotto/blob/5aaf6d2a9d737be166577878c369fb5cd9b6e1a0/src/main/java/lotto/type/LottoMatchType.java" target="_blank">당첨 순위 Enum 타입 처리</a></li>
<li><a href="https://woowabros.github.io/tools/2017/07/10/java-enum-uses.html" target="_blank">우아한형제들 기술블로그 Java Enum 활용기</a></li>
</ul>
<br>

<h2 id="jdk---stream-api---2">[JDK] - Stream API - 2</h2>
<h3 id="내용-6">내용</h3>
<ul>
<li>모든 함수의 인덴트를 1로 맞추고, 가독성 좋은 코드를 작성하기 위해 Stream API를 적극 활용했다.<h3 id="링크-5">링크</h3>
</li>
<li><a href="https://github.com/woowacourse/java-lotto/blob/5aaf6d2a9d737be166577878c369fb5cd9b6e1a0/src/main/java/lotto/type/LottoMatchType.java" target="_blank">LottoMatchType의 Stream API 적용</a></li>
<li><a href="https://github.com/woowacourse/java-lotto/blob/5aaf6d2a9d737be166577878c369fb5cd9b6e1a0/src/main/java/lotto/view/InputView.java" target="_blank">InputView의 Stream API 적용</a></li>
<li><a href="https://github.com/woowacourse/java-lotto/blob/5aaf6d2a9d737be166577878c369fb5cd9b6e1a0/src/main/java/lotto/view/printer/OutputPrinter.java" target="_blank">OutputPrinter의 Stream API 적용</a></li>
<li><a href="https://github.com/woowacourse/java-lotto/blob/5aaf6d2a9d737be166577878c369fb5cd9b6e1a0/src/main/java/lotto/domain/ticketresult/LottoResult.java" target="_blank">LottoResult의 Stream API 적용</a></li>
</ul>
<br>

<h2 id="oop---domain-validation---2">[OOP] - Domain validation - 2</h2>
<h3 id="내용-7">내용</h3>
<ul>
<li><p>Validation을 최대한 도메인 객체 내부의 생성자에서 했다.</p>
</li>
<li><p>아무리 생각해도 도메인 내부에 어울리지 않는 Validation 작업들은 도메인 외부 InputView의 함수에서 했다.</p>
<h3 id="링크-6">링크</h3>
</li>
<li><p><a href="https://github.com/woowacourse/java-lotto/blob/5aaf6d2a9d737be166577878c369fb5cd9b6e1a0/src/main/java/lotto/domain/LottoNumber.java" target="_blank">LottoNumber 생성자 Validation</a></p>
</li>
<li><p><a href="https://github.com/woowacourse/java-lotto/blob/5aaf6d2a9d737be166577878c369fb5cd9b6e1a0/src/main/java/lotto/domain/LottoTicket.java" target="_blank">LottoTicket 생성자 Validation</a></p>
</li>
<li><p><a href="https://github.com/woowacourse/java-lotto/blob/5aaf6d2a9d737be166577878c369fb5cd9b6e1a0/src/main/java/lotto/domain/ticketpurchase/UserPurchase.java" target="_blank">UserPurchase 생성자 Validation</a></p>
</li>
<li><p><a href="https://github.com/woowacourse/java-lotto/blob/5aaf6d2a9d737be166577878c369fb5cd9b6e1a0/src/main/java/lotto/domain/ticketresult/WinningLottoNumbers.java" target="_blank">WinningLottoNumbers 생성자 Validation</a></p>
</li>
<li><p><a href="https://github.com/woowacourse/java-lotto/blob/5aaf6d2a9d737be166577878c369fb5cd9b6e1a0/src/main/java/lotto/view/InputView.java" target="_blank">InputView Validation</a></p>
</li>
</ul>
<br>

<h2 id="jdk---os-독립적-개행문자---2">[JDK] - OS 독립적 개행문자 - 2</h2>
<h3 id="내용-8">내용</h3>
<ul>
<li>OS마다 개행문자가 다르다는것을 간과하고 코드에 개행문자를 <strong>\n</strong> 로 지정했다.</li>
<li>리뷰어 휴가 이에 대한 피드백으로 <strong>System.lineSeparator()</strong> 에 대해 찾아보라고해서 오라클 자바 문서를 찾아보았다.</li>
<li>해당 함수는 운영체제에 맞는 개행문자를 반환해준다. 즉, OS로부터 독립적인 개발을 할 수 있는 것이다. 이런게 있었다니!! 엄청난 꿀팁이다!!<h3 id="링크-7">링크</h3>
</li>
<li><a href="https://docs.oracle.com/javase/7/docs/api/java/lang/System.html" target="_blank">오라클 자바 문서 System.lineSeparator()</a></li>
</ul>
<hr>
<br>

<h1 id="🚀-로또---2단계---수동-구매">🚀 로또 - 2단계 - 수동 구매</h1>
<hr>
<h2 id="jdk-map-api---1">[JDK] Map API - 1</h2>
<h3 id="내용-9">내용</h3>
<ul>
<li>리뷰어 휴의 피드백을 받아, Map에서 get()으로 꺼내 값을 증가시킨 후 다시 put()으로 저장하도록 작성했던 코드를, Map API computeIfPresent() 를 사용해 더욱 명시적이고 우아하게 바꿨다.</li>
</ul>
<h3 id="링크-8">링크</h3>
<ul>
<li><a href="http://tech.javacafe.io/2018/12/03/HashMap/" target="_blank">자바 HashMap을 효과적으로 사용하는 방법</a></li>
<li><a href="https://github.com/woowacourse/java-lotto/blob/6ba6f49c152acc460a7c4516fee685d8d13b9419/src/main/java/lotto/domain/ticketresult/LottoResult.java" target="_blank">LottoResult.java 의 increaseOneCountOfLottoMatchType()</a></li>
</ul>
<br>

<h2 id="tdd-테스트-코드-네이밍-컨벤션---2">[TDD] 테스트 코드 네이밍 컨벤션 - 2</h2>
<h3 id="내용-10">내용</h3>
<ul>
<li>페어가 테스트 코드 함수 네이밍 컨벤션 적용을 제시하여, 이에 대해 찾아보고 적용했다.</li>
<li>훨씬 더 의도를 파악하기 쉬워졌다.</li>
</ul>
<h3 id="링크-9">링크</h3>
<ul>
<li><a href="https://m.blog.naver.com/PostView.nhn?blogId=genycho&logNo=220317775484&proxyReferer=https:%2F%2Fwww.google.com%2F" target="_blank">유명한 7가지 단위테스트 네이밍 컨벤션</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-lotto/tree/step2/src/test/java/lotto" target="_blank">테스트 코드들</a></li>
</ul>
<br>

<h2 id="markdown-체크박스-적용---1">[Markdown] 체크박스 적용 - 1</h2>
<h3 id="내용-11">내용</h3>
<ul>
<li><p>리뷰어 휴가 README.md 파일의 기능구현 목록에 체크박스 적용을 제안해, 적용했다.</p>
</li>
<li><p>구현이 완료된 것들과 아직 완료되지 않은 것들이 직관적으로 구별되어 구현 진행과 관리가 더욱 편해졌다.</p>
</li>
</ul>
<h3 id="링크-10">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-lotto/blob/step2/README.md" target="_blank">README.md</a></li>
<li><a href="https://jihyehwang09.github.io/2019/05/06/TIL-2019-05-06/" target="_blank">마크다운(markdown) 문서에 체크박스 추가하기</a></li>
</ul>
<br>

<h2 id="naming-명확한-이름---4">[Naming] 명확한 이름 - 4</h2>
<h3 id="내용-12">내용</h3>
<ul>
<li><p>모든 이름들을 영어 문법을 지키면서 최대한 구체적으로 자세하게 지으려다 보니,
지나치게 길어지고 중복되는 부분들이 생겨, 오히려 의미전달력이 떨어지는 현상이 발생했다.</p>
</li>
<li><p>리뷰어 휴가 공유해 준 영어권에서 작성된 글을 참고하여
최대한 직관적으로 명확하게 알아볼 수 있도록 네이밍을 변경했다.</p>
</li>
<li><p>문법을 지킨 길고 자세한 이름들 보다 더욱 직관적으로 알아보기 쉬워졌고, 코드가 깨끗해졌다.</p>
</li>
</ul>
<h3 id="링크-11">링크</h3>
<ul>
<li><a href="https://medium.com/better-programming/a-useful-framework-for-naming-your-classes-functions-and-variables-e7d186e3189f" target="_blank">A Useful Framework for Naming Your Classes, Functions, and Variables</a></li>
</ul>
<br>

<h2 id="oop-caching---3">[OOP] Caching - 3</h2>
<h3 id="내용-13">내용</h3>
<ul>
<li><p>코치 제이슨이 로또번호 객체 캐싱을 제안해, 이를 적용했다.</p>
</li>
<li><p>반복되어 사용되는 객체(로또번호)들을 캐싱해 재사용하였다.</p>
</li>
<li><p>new 연산자를 통해 계속 생성할 필요 없이 캐싱되어있는 로또번호들을 가져다가 쓰니
메모리도 절약되고, 코드도 깔끔해졌다.</p>
</li>
</ul>
<h3 id="링크-12">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-lotto/blob/step2/src/main/java/lotto/domain/LottoNumbers.java" target="_blank">LottoNumbers.java</a></li>
</ul>
<br>

<h2 id="jdk-custom-exception---2">[JDK] Custom Exception - 2</h2>
<h3 id="내용-14">내용</h3>
<ul>
<li><p>리뷰어 휴가 원시값에 대한 유효성 검사 시, 예외에 Custom Exception을 적용해볼 것을 제안해 적용다.</p>
</li>
<li><p>예외가 더욱 명확해져, 다른 예외들과 직관적인 구분이 가능해졌다.</p>
</li>
</ul>
<h3 id="링크-13">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-lotto/tree/step2/src/main/java/lotto/domain/exception" target="_blank">Custom Exceptions</a></li>
<li><a href="https://www.baeldung.com/java-new-custom-exception" target="_blank">Create a Custom Exception in Java</a></li>
</ul>
<br>

<h2 id="oop-caching---3-1">[OOP] Caching - 3</h2>
<h3 id="내용-15">내용</h3>
<ul>
<li><p>리뷰어 휴의 제안으로 <strong>Integer.valueOf() API</strong> 를 참고하여 캐싱 방식을 개선했다.</p>
</li>
<li><p><strong>LottoNumber.valueOf()</strong> 와 같이 API 사용 형식이 더욱 깔끔하고 명확해졌다. 의미 전달력도 매우 향상되었다.</p>
</li>
</ul>
<h3 id="링크-14">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-lotto/blob/step2/src/main/java/lotto/domain/LottoNumber.java" target="_blank">LottoNumber.java</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-lotto/blob/step2/src/main/java/lotto/domain/LottoNumbers.java" target="_blank">LottoNumbers.java</a></li>
<li><a href="https://meetup.toast.com/posts/185" target="_blank">[Java] Integer.valueOf(127) == Integer.valueOf(127) 는 참일까요?</a></li>
</ul>
<br>

<h2 id="jdk-bigdecimal-api---3">[JDK] BigDecimal API - 3</h2>
<h3 id="내용-16">내용</h3>
<ul>
<li><p>수익률 소수점 계산에서 <strong>BigDecimal API</strong> 사용이 필요했다.</p>
</li>
<li><p>사용법을 제대로 숙지하지 않은 채 사용해, 소수점 셋 째 자리에서 반올림이 되지 않는 버그가 발생했다.</p>
</li>
<li><p><strong>BigDecimal API 문서</strong> 와 구글링을 참고하고, <strong>학습 테스트</strong> 를 통해 정확한 API 사용법을 익혀 버그를 고쳤다. </p>
</li>
<li><p>맨 처음 구현할 때, 구글링을 통해 찾은 <strong>잘못된 자료</strong> 를 참고해서 코드를 작성했다. 사용법이 애매할 때는 <strong>학습 테스트</strong> 로 제대로 익힌 뒤에 사용해야 겠다.</p>
</li>
</ul>
<h3 id="링크-15">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-lotto/blob/step2/src/main/java/lotto/domain/ticketresult/LottoResult.java" target="_blank">LottoResult.java</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-lotto/blob/step2/src/test/java/study/bigdecimal/BigDecimalTest.java" target="_blank">BigDecimalTest.java</a></li>
<li><a href="https://www.geeksforgeeks.org/bigdecimal-divide-method-in-java-with-examples/" target="_blank">BigDecimal divide() Method in Java with Examples</a></li>
<li><a href="https://stackoverflow.com/questions/10637232/how-can-i-divide-properly-using-bigdecimal" target="_blank">How can I divide properly using BigDecimal</a></li>
<li><a href="https://jsonobject.tistory.com/466" target="_blank">Java, BigDecimal 사용법 정리</a></li>
</ul>
<hr>
<br>

<h1 id="🚀-블랙잭---1단계">🚀 블랙잭 - 1단계</h1>
<hr>
<h2 id="oop-추상클래스---5">[OOP] 추상클래스 - 5</h2>
<h3 id="내용-17">내용</h3>
<ul>
<li>도메인들의 공통 부분을 추상클래스로 뽑아 추상화 했다.</li>
<li>중복이 사라지고, 자식클래스들이 매우 간단해졌다.</li>
<li>인터페이스를 추상클래스에서 implements하면 무조건 인터페이스의 모든 메소드들을 구현해야 하는 줄 알았는데, 인터페이스의 일부 메소드들을 추상메소드로 둘 수 있음을 배웠다.</li>
</ul>
<h3 id="링크-16">링크</h3>
<ul>
<li><p><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/rentcompany/car/AbstractCar.java" target="_blank">AbstractCar.java</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/player/AbstractPlayer.java" target="_blank">AbstractPlayer.java</a></p>
</li>
</ul>
<br>

<h2 id="oop-인터페이스---5">[OOP] 인터페이스 - 5</h2>
<h3 id="내용-18">내용</h3>
<ul>
<li>공통 메소드들을 인터페이스로 뽑아 추상화 했다.</li>
</ul>
<h3 id="링크-17">링크</h3>
<ul>
<li><p><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/rentcompany/car/Car.java" target="_blank">Car.java</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/player/Player.java" target="_blank">Player.java</a></p>
</li>
</ul>
<br>

<h2 id="tdd-테스트-주도-개발---5">[TDD] 테스트 주도 개발 - 5</h2>
<h3 id="내용-19">내용</h3>
<ul>
<li>TDD를 통해 메시지(책임)로부터 시작하여 그에 따라 필요한 속성들이 자연스럽게 정해지는 OOP를 구현했다.</li>
<li>이는 <strong>책 [객체지향의 사실과 오해]</strong> 를 읽고 적용한 것이다.</li>
<li>TDD로 구현하니 테스트 할 수 있는 코드를 작성하게 되었다.</li>
<li>적절한 단위 테스트 케이스 작성을 통해, 안전하고 점진적인 리팩토링을 할 수 있었다.</li>
</ul>
<h3 id="링크-18">링크</h3>
<ul>
<li><a href="http://www.yes24.com/Product/Goods/18249021" target="_blank">객체지향의 사실과 오해</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/tree/step1/src/test/java">테스트 코드들</a></li>
</ul>
<br>

<h2 id="jdk-enum---3">[JDK] Enum - 3</h2>
<h3 id="내용-20">내용</h3>
<ul>
<li><p>Enum을 사용해 관련된 상수들을 하나로 묶어 명확하고 편리하게 활용했다.</p>
</li>
<li><p>상수와 관련된 값들을 같이 묶어 활용했다.</p>
</li>
</ul>
<h3 id="링크-19">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/card/type/CardNumberType.java" target="_blank">CardNumberType.java</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/card/type/CardShapeType.java" target="_blank">CardShapeType.java</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/ResultType.java" target="_blank">ResultType.java</a></li>
</ul>
<br>

<h2 id="디자인-패턴-전략패턴---4">[디자인 패턴] 전략패턴 - 4</h2>
<h3 id="내용-21">내용</h3>
<ul>
<li><p>딜러가 몇 장의 카드를 반환할지 결정하는 데에 전략패턴을 사용했다.</p>
</li>
<li><p>전략패턴을 적용하면 최소한의 코드 수정으로 전략을 다르게 가져갈 수 있고, 확장에도 자유로움을 알 수 있었다.</p>
</li>
</ul>
<h3 id="링크-20">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/player/strategy/CardOpenStrategy.java">CardOpenStrategy.java</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/player/strategy/OneCardOpenStrategy.java">OneCardOpenStrategy.java</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/player/strategy/AllCardsOpenStrategy.java">AllCardsOpenStrategy.java</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/player/Dealer.java">Dealer.java</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/controller/BlackJackController.java">BlackJackController.java</a></li>
</ul>
<br>

<h2 id="디자인-패턴-싱글톤-패턴---2">[디자인 패턴] 싱글톤 패턴 - 2</h2>
<h3 id="내용-22">내용</h3>
<ul>
<li><p>페어 멍토의 제안으로, 애플리케이션 전체에서 공유되는 카드 뭉치 객체에 싱글톤 패턴을 적용했다.</p>
</li>
<li><p>애플리케이션 전체에서 공유되는 객체에 대해서는 static 적용만 해왔는데, 싱글톤 패턴 적용에 대해 새롭게 배웠다.</p>
</li>
</ul>
<h3 id="링크-21">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/card/Cards.java" target="_blank">Cards.java</a></li>
</ul>
<br>

<h2 id="oop-정적-팩토리-메서드---3">[OOP] 정적 팩토리 메서드 - 3</h2>
<h3 id="내용-23">내용</h3>
<ul>
<li>new 연산자는 단순히 생성밖에 못한다.</li>
<li>정적 팩토리 메서드를 이용하면, 호출하는 메서드에 따라 로직을 적용해 다른 생성 결과를 낼 수 있다.</li>
</ul>
<h3 id="링크-22">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-blackjack/blob/step1/src/main/java/blackjack/domain/card/Cards.java">Cards.java</a></li>
</ul>
<hr>
<br>

<h1 id="🚀-체스---1-2-3단계">🚀 체스 - 1, 2, 3단계</h1>
<hr>
<h2 id="oop-추상클래스---5-1">[OOP] 추상클래스 - 5</h2>
<h3 id="내용-24">내용</h3>
<ul>
<li>도메인들의 공통 부분들을 추상클래스로 뽑아 추상화 했다. </li>
<li>중복이 사라지고, 자식클래스들이 매우 간단해졌다.</li>
<li>메서드 오버라이딩을 통한 다형성을 구현했다.</li>
</ul>
<h3 id="링크-23">링크</h3>
<ul>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/Piece.java">Piece</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/board/setting/BoardSetting.java">BoardSetting</a></p>
<br>

</li>
</ul>
<h2 id="oop-dto---2">[OOP] DTO - 2</h2>
<h3 id="내용-25">내용</h3>
<ul>
<li><p>Domain, Controller, View간의  의존성을 분리하기 위해 DTO를 사용했다.</p>
</li>
<li><p>필요한 데이터들을 알맞게 담아 넘겨줄 수 있었다.</p>
<h3 id="링크-24">링크</h3>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/controller/dto/request/CommandRequestDTO.java">CommandRequestDTO</a></p>
</li>
</ul>
<br>

<h2 id="oop-의존성-주입---4">[OOP] 의존성 주입 - 4</h2>
<h3 id="내용-26">내용</h3>
<ul>
<li><p>체스 보드 세팅을 외부에서 주입하도록 했다.</p>
</li>
<li><p>테스트 코드에서 Custom한 기물 배치를 할 수 있도록 해, 테스트에 용이한 구조를 만들었다.</p>
<h3 id="링크-25">링크</h3>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/board/setting/BoardSetting.java">BoardSetting</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/board/setting/BoardDefaultSetting.java">BoardDefaultSetting</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/board/setting/BoardCustomSetting.java">BoardCustomSetting</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/test/java/chess/domain/game/ChessGameTest.java">ChessGameTest</a></p>
</li>
</ul>
<br>

<h2 id="jdk-enum---5">[JDK] Enum - 5</h2>
<h3 id="내용-27">내용</h3>
<ul>
<li><p>Enum 클래스로 상수들을 종류별로 묶어서 관리했다.</p>
</li>
<li><p>상수들을 종류별로 편리하게 관리하고 사용할 수 있었다.</p>
</li>
<li><p>한 Enum 클래스에서 다른 Enum 클래스의 상수들을 묶어 활용하는 것을 경험할 수 있었다.</p>
<h3 id="링크-26">링크</h3>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/position/type/File.java">File</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/position/type/Rank.java">Rank</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/controller/type/Command.java">Command</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/type/Direction.java">Direction</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/type/PieceType.java">PieceType</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/type/PieceWithColorType.java">PieceWithColorType</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/player/type/TeamColor.java">TeamColor</a></p>
</li>
</ul>
<br>

<h2 id="oop-일급-컬렉션---3">[OOP] 일급 컬렉션 - 3</h2>
<h3 id="내용-28">내용</h3>
<ul>
<li><p>컬렉션을 클래스로 포장해 일급 컬렉션으로 만들었다.</p>
</li>
<li><p>함수를 통해 컬렉션 활용을 다양하게 할 수 있었다.</p>
</li>
<li><p>캡슐화를 하여 컬렉션의 남용을 방지했다.</p>
<h3 id="링크-27">링크</h3>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/board/Board.java">Board</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/position/cache/PositionsCache.java">PositionsCache</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/cache/PiecesCache.java">PiecesCache</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/player/Players.java">Players</a></p>
</li>
</ul>
<br>

<h2 id="oop-caching---4">[OOP] Caching - 4</h2>
<h3 id="내용-29">내용</h3>
<ul>
<li><p>체스 보드 칸의 위치들과 기물들을 캐싱했다.</p>
</li>
<li><p>객체를 새로 생성하지 않고 재사용해, 메모리를 절약할 수 있었다.</p>
</li>
<li><p>static 메서드를 사용해 객체를 편리하게 가져올 수 있었다.</p>
<h3 id="링크-28">링크</h3>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/position/cache/PositionsCache.java">PositionsCache</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/cache/PiecesCache.java">PiecesCache</a></p>
</li>
</ul>
<br>

<h2 id="junit-nested-어노테이션---5">[JUnit] Nested 어노테이션 - 5</h2>
<h3 id="내용-30">내용</h3>
<ul>
<li><p>동일한 맥락의 테스트 코드들을 Nested 어노테이션을 사용해 묶었다.</p>
</li>
<li><p>Nested 어노테이션을 사용하지 않을 때는 연관된 테스트들끼리 구별하기가 어려웠는데, 이를 사용하니 훨씬 편해졌다.</p>
</li>
<li><p>@BeforeEach, @AfterEach의 적용 범위가 해당 어노테이션이 있는 클래스 범위에 한정되는 장점을 활용할 수 있었다.</p>
<h3 id="링크-29">링크</h3>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/test/java/chess/domain/game/ChessGameTest.java">ChessGameTest</a></p>
</li>
</ul>
<br>

<h2 id="test-테스트하기-쉬운-코드---5">[Test] 테스트하기 쉬운 코드 - 5</h2>
<h3 id="내용-31">내용</h3>
<ul>
<li><p>처음에는 테스트 코드를 체스 보드의 위치값 문자열로만 테스트해, 작성하기도 어렵고 이해하기도 어려웠다.</p>
</li>
<li><p>테스트하기 쉬운 코드로 리팩토링 하고 나니 테스트 코드가 직관적이 되어 이해하기도, 작성하기도 쉬워졌다.</p>
<h3 id="링크-30">링크</h3>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/test/java/chess/domain/game/ChessGameTest.java">ChessGameTest</a></p>
</li>
</ul>
<br>

<h2 id="jdk-stream---3">[JDK] Stream - 3</h2>
<h3 id="내용-32">내용</h3>
<ul>
<li><p>Stream 을 적극 활용해 컬렉션을 자유롭게 다루었다.</p>
</li>
<li><p>함수의 개수와 depth를 효과적으로 줄일 수 있었다.</p>
<h3 id="링크-31">링크</h3>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/position/type/File.java">File</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/position/type/Rank.java">Rank</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/board/Board.java">Board</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/controller/type/Command.java">Command</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/cache/PiecesCache.java">PiecesCache</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/type/Direction.java">Direction</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/Pieces.java">Pieces</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/player/Players.java">Players</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/position/cache/PositionsCache.java">PositionsCache</a></p>
</li>
</ul>
<hr>
<br>


<h1 id="🚀-체스---4-5단계">🚀 체스 - 4, 5단계</h1>
<hr>
<h2 id="oop-dto---5">[OOP] DTO - 5</h2>
<h3 id="내용-33">내용</h3>
<ul>
<li>Domain, Controller, View 간의 데이터 전달에 DTO를 사용했다.</li>
<li>필요한 데이터들을 알맞은 형태로 담아 전달했다.</li>
<li>DTO를 통해 Layer들 간의 결합도를 낮췄다.</li>
</ul>
<h3 id="링크-32">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-chess/blob/step2/src/main/java/chess/controller/console/dto/CommandRequestDTO.java">CommandRequestDTO</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-chess/tree/step2/src/main/java/chess/controller/dto">다른 DTO들</a></li>
</ul>
<br>

<h2 id="oop-caching---5">[OOP] Caching - 5</h2>
<h3 id="내용-34">내용</h3>
<ul>
<li>DB에 체스 보드 모든 칸들의 위치 정보들과 모든 색깔별 기물들의 정보를 저장해 두었다.</li>
<li>애플리케이션이 실행될 때 이 정보들을 DB로부터 불러와, 객체 형태로 애플리케이션 내에 캐싱했다.</li>
<li>자주 쓰이는 값들을 캐싱해 DB 쿼리 조회 수를 많이 줄여, 성능이 매우 개선됐다.</li>
<li>애플리케이션 내에서 해당 객체들을 사용하기 편했다.</li>
</ul>
<h3 id="링크-33">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-chess/blob/step2/src/main/java/chess/domain/position/cache/PositionsCache.java">PositionsCache</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-chess/blob/step2/src/main/java/chess/domain/piece/cache/PiecesCache.java">PiecesCache</a></li>
</ul>
<br>


<h2 id="oop-점진적-리팩토링---5">[OOP] 점진적 리팩토링 - 5</h2>
<h3 id="내용-35">내용</h3>
<ul>
<li>Web View UI 와 DB를 적용하기 위해 많은 변경이 필요했다.</li>
<li>안전하게 점진적 리팩토링을 했다.</li>
<li>항상 모든 테스트코드들이 통과하도록 했다.</li>
<li>리팩토링에 확신을 갖고 자유롭게 할 수 있었다.</li>
<li>자주 테스트 하고 싶어졌다.</li>
</ul>
<br>


<h2 id="sql-query-최적화---5">[SQL] Query 최적화 - 5</h2>
<h3 id="내용-36">내용</h3>
<ul>
<li>DB에서 필요한 데이터들만 조회하는 쿼리를 작성했다.</li>
<li>원하는 데이터를 조회하기 위해 쿼리를 여러 번 날리지 않고, JOIN 등을 사용해 한 방 쿼리로 가져왔다.</li>
<li>요청 쿼리 수가 확연히 줄어 성능이 많이 개선됐다.</li>
</ul>
<h3 id="링크-34">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-chess/blob/step2/src/main/java/chess/dao/playerpieceposition/PlayerPiecePositionDAO.java">PlayerPiecePositionDAO</a></li>
</ul>
<br>


<h2 id="oop-domain-의존성-분리---5">[OOP] Domain 의존성 분리 - 5</h2>
<h3 id="내용-37">내용</h3>
<ul>
<li>이전에는 <code>Board</code>가 <code>Piece</code>를 알고, <code>Piece</code>가 <code>Board</code>를 알고 있었다.</li>
<li>이는 서로 의존성이 높음을 의미한다.</li>
<li>기물 이동 요청에 대해 이동이 가능한지 판단하는 책임을  <code>MoveChecker</code> 객체로 완전히 분리했다.</li>
<li><code>Board</code>와 <code>Piece</code>간의 의존성을 현저히 낮출 수 있었다.</li>
<li>이전에는 플레이어의 점수 계산을 <code>Pieces</code> 도메인에서 했다.</li>
<li>점수 계산 책임을 <code>ScoreCalculator</code> 객체로 분리했다.</li>
</ul>
<h3 id="링크-35">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-chess/blob/step2/src/main/java/chess/domain/board/move/MoveChecker.java">MoveChecker</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-chess/blob/step2/src/main/java/chess/domain/player/score/ScoreCalculator.java">ScoreCalculator</a></li>
</ul>
<br>


<h2 id="db-테이블-연관관계-매핑---5">[DB] 테이블 연관관계 매핑 - 5</h2>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/21925828-b327-44e8-bb05-c240073c06c3/ERD_%EC%BA%A1%EC%B3%90.PNG" alt=""></p>
<h3 id="내용-38">내용</h3>
<ul>
<li>인프런에서 김영한 님의 JPA 강좌에서 공부했던 내용들을 기반으로 DB 테이블 연관관계를 외래키를 통해 매핑했다.</li>
<li>N : 1 (다대일) 관계를 실무에서 제일 많이 쓰고, N : M (다대다) 관계는 중간에 N : 1 (다대일), 1 : M (다대일) 관계의 중간 테이블을 만들어 풀어내야 한다고 하셨다. 그래야 매핑 관리에 유리하고, 각 매핑에 대한 추가적인 정보를 중간 매핑 테이블에 저장할 수 있기 때문이다.</li>
<li>김영한 님의 강의 내용을 바탕으로 위와 같이 DB 스키마를 설계했다.</li>
</ul>
<hr>
<br>

<h1 id="lv2">Lv2</h1>
<hr>
<h1 id="🚀-spring-체스---1-2단계">🚀 Spring 체스 - 1, 2단계</h1>
<hr>
<h2 id="spring-di의존성-주입-적용---4">[Spring] DI(의존성 주입) 적용 - 4</h2>
<h3 id="내용-39">내용</h3>
<ul>
<li>기존 코드는 객체들이 서로 강하게 결합되어 있었다.</li>
<li>Spring Bean 생성자 주입 방식으로, 모든 객체들 간의 결합도를 낮췄다.</li>
</ul>
<h3 id="태그">태그</h3>
<p>Spring, DI, OOP</p>
<br>

<h2 id="spring-web-mvc-적용---3">[Spring] Web MVC 적용 - 3</h2>
<h3 id="내용-40">내용</h3>
<ul>
<li>Spark Java를 제거하고, Spring Web MVC를 적용했다.</li>
<li>추상화가 잘 되어있어 Controller의 코드가 매우 간결하고 깔끔해졌다.</li>
</ul>
<h3 id="태그-1">태그</h3>
<p>Spring, MVC</p>
<br>

<h2 id="jdbc-jdbctemplate-적용---4">[JDBC] JdbcTemplate 적용 - 4</h2>
<h3 id="내용-41">내용</h3>
<ul>
<li><code>Mysql Jdbc Driver</code> 만 사용해서 작성했던 DAO 클래스들에 <code>JdbcTemplate</code> 를 적용했다.</li>
<li>한 단계 더 추상화 되어있어, 코드가 많이 줄어들고 깔끔해졌다.</li>
<li><code>queryForObject()</code> 로 DB에서 값을 조회할 때, 결괏값의 개수가 0개이면 <code>EmptyResultDataAccessException</code> 가 발생해, <code>try ~ catch</code> 문으로 직접 예외를 처리해야 하는 부분이 아쉬웠다.</li>
</ul>
<h3 id="태그-2">태그</h3>
<p>JDBC</p>
<br>

<h2 id="oop-도메인-분리---5">[OOP] 도메인 분리 - 5</h2>
<h3 id="내용-42">내용</h3>
<ul>
<li>기존 코드는 <strong>도메인</strong> 과 <strong>Spring과 관련된 Repository</strong> 들이 모두 얽혀있었다.</li>
<li>모든 도메인이 Spring에 의존하고 있었다.</li>
<li>테스트도 <strong>@SpringBootTest</strong> 를 사용해 Spring 자체를 실행시켜야 가능했다.</li>
<li>코드 리뷰어 <strong>김고래</strong> 의 피드백을 적용하면서, 도메인은 순수 자바 객체로 유지되어야 함을 깨달았다.</li>
<li>불가피하게 Spring을 띄워야 하는 Repository 관련 테스트를 제외하고, 도메인 관련 테스트들은 모두 순수 자바로만 실행할 수 있게 되었다. 테스트 속도가 매우 빨라졌다.</li>
<li>도메인 자체만 테스트하다보니, 각 객체별로 작은 단위의 테스트를 매우 간단하게 할 수 있었다. 이전에는 모든 의존성을 알맞게 주입해 놓아야 테스트가 가능했다.</li>
<li>콘솔 게임은 Spring 없이 순수하게 자바로만 실행할 수 있게 되었다.</li>
</ul>
<h3 id="태그-3">태그</h3>
<p>OOP, Domain, Test, Spring</p>
<br>

<h2 id="test-in-memory-db-적용---3">[Test] In-Memory DB 적용 - 3</h2>
<h3 id="내용-43">내용</h3>
<ul>
<li>이전에는 테스트를 실제 DB를 통해서 했었다.</li>
<li>코드 리뷰어 <strong>김고래</strong> 의 피드백으로 Test들은 In-Memory DB인 H2 DB를 적용했다.</li>
<li>테스트가 훨씬 가벼워져 속도가 빨라지고, 외부 DB에 의존하지 않게 되었다.</li>
</ul>
<h3 id="태그-4">태그</h3>
<p>Test, DB</p>
<hr>
<br>

<h1 id="🚀-spring-체스---3단계-추가미션">🚀 Spring 체스 - 3단계, 추가미션</h1>
<hr>
<h2 id="spring-mvc-restcontrolleradvice---3">[Spring MVC] @(Rest)ControllerAdvice - 3</h2>
<h3 id="내용-44">내용</h3>
<ul>
<li>Controller와 Exception의 범위를 지정하면, Exception을 알아서 잡아준다.</li>
<li>try ~ catch 문을 없앨 수 있어, 비즈니스 로직이 매우 깔끔해진다.</li>
</ul>
<h3 id="태그-5">태그</h3>
<p>Spring, MVC, Exception</p>
<br>

<h2 id="http-rest-api---2">[HTTP] REST API - 2</h2>
<h3 id="내용-45">내용</h3>
<ul>
<li>삭제 요청을 POST &quot;/delete&quot; 로 했었다.</li>
<li>DELETE &quot;games/${gameId}&quot; 로 바꾸니, API가 더욱 명시적으로 됐다.</li>
</ul>
<h3 id="태그-6">태그</h3>
<p>HTTP, REST, API</p>
<br>

<h2 id="http-cookie---4">[HTTP] Cookie - 4</h2>
<h3 id="내용-46">내용</h3>
<ul>
<li>클라이언트에 값을 저장하고, 이후 요청마다 해당 값을 포함시키는데 적합한 쿠키를 사용했다.</li>
<li>한 번 쿠키를 발급해주면, 만료되기 전 까지 매 요청에 쿠키를 포함해 보내기 때문에 인증에 유용하다.</li>
</ul>
<h3 id="태그-7">태그</h3>
<p>HTTP, Cookie</p>
<br>

<h2 id="보안-hash-암호화---4">[보안] Hash 암호화 - 4</h2>
<h3 id="내용-47">내용</h3>
<ul>
<li>프로그래밍 요구사항에서 비밀번호를 클라이언트에 저장하고, 매 요청에 같이 보내라는 부분이 있었다.</li>
<li>이 방식은 보안에 매우 취약하기 때문에, Hash 암호화를 사용했다.</li>
<li>쿠키나 요청 데이터가 탈취당하더라도 Hash 함수로 암호화 되어있기 때문에, 암호 본문은 알 수 없어 보안에 유리하다.</li>
</ul>
<h3 id="태그-8">태그</h3>
<p>보안, Hash</p>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[우아한테크코스 3기 백엔드 Lv2 [체스 - 3단계, 추가미션] 학습로그]]></title>
            <link>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv2-%EC%B2%B4%EC%8A%A4-3%EB%8B%A8%EA%B3%84-%EC%B6%94%EA%B0%80%EB%AF%B8%EC%85%98-%EC%B5%9C%EC%B4%88-PR-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8</link>
            <guid>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv2-%EC%B2%B4%EC%8A%A4-3%EB%8B%A8%EA%B3%84-%EC%B6%94%EA%B0%80%EB%AF%B8%EC%85%98-%EC%B5%9C%EC%B4%88-PR-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8</guid>
            <pubDate>Mon, 19 Apr 2021 18:14:05 GMT</pubDate>
            <description><![CDATA[<h2 id="pull-request"><a href="https://github.com/woowacourse/jwp-chess/pull/271">Pull Request</a></h2>
<br>

<h2 id="spring-mvc-restcontrolleradvice---3">[Spring MVC] @(Rest)ControllerAdvice - 3</h2>
<h3 id="내용">내용</h3>
<ul>
<li>모든 컨트롤러에서 발생하는 예외를 catch할 수 있다.</li>
<li>패키지, Controller, Exception의 범위를 지정할 수도 있다.</li>
<li>try ~ catch 문을 없앨 수 있어, 비즈니스 로직이 매우 깔끔해진다.</li>
</ul>
<h3 id="태그">태그</h3>
<p>Spring, MVC, Exception</p>
<br>

<h2 id="http-rest-api---2">[HTTP] REST API - 2</h2>
<h3 id="내용-1">내용</h3>
<ul>
<li>삭제 요청을 POST &quot;/delete&quot; 로 했었다.</li>
<li>DELETE &quot;games/${gameId}&quot; 로 바꾸니, API가 더욱 명시적으로 되었다.</li>
</ul>
<h3 id="태그-1">태그</h3>
<p>HTTP, REST, API</p>
<br>

<h2 id="http-cookie---4">[HTTP] Cookie - 4</h2>
<h3 id="내용-2">내용</h3>
<ul>
<li>클라이언트에 값을 저장하고, 이후 요청마다 해당 값을 포함시키는데 적합한 쿠키를 사용했다.</li>
<li>한 번 쿠키를 발급해주면, 만료되기 전 까지 매 요청에 쿠키를 포함해 보내기 때문에 인증에 유용하다.</li>
</ul>
<h3 id="태그-2">태그</h3>
<p>HTTP, Cookie</p>
<br>

<h2 id="보안-hash-암호화---4">[보안] Hash 암호화 - 4</h2>
<h3 id="내용-3">내용</h3>
<ul>
<li>프로그래밍 요구사항에서 비밀번호를 클라이언트에 저장하고, 매 요청에 같이 보내라는 부분이 있었다.</li>
<li>이 방식은 보안에 매우 취약하기 때문에, Hash 암호화를 사용했다.</li>
<li>쿠키나 요청 데이터가 탈취당하더라도 Hash 함수로 암호화 되어있기 때문에, 암호 본문은 알 수 없어 보안에 유리하다.</li>
</ul>
<h3 id="태그-3">태그</h3>
<p>보안, Hash</p>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[우아한테크코스 3기 백엔드 Lv2 [체스 - 1, 2단계] 1차 코드리뷰 적용 학습로그]]></title>
            <link>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv2-%EC%B2%B4%EC%8A%A4-1-2%EB%8B%A8%EA%B3%84-1%EC%B0%A8-%EC%BD%94%EB%93%9C%EB%A6%AC%EB%B7%B0-%EC%A0%81%EC%9A%A9-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8</link>
            <guid>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv2-%EC%B2%B4%EC%8A%A4-1-2%EB%8B%A8%EA%B3%84-1%EC%B0%A8-%EC%BD%94%EB%93%9C%EB%A6%AC%EB%B7%B0-%EC%A0%81%EC%9A%A9-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8</guid>
            <pubDate>Sat, 17 Apr 2021 09:54:14 GMT</pubDate>
            <description><![CDATA[<h2 id="pull-request"><a href="https://github.com/woowacourse/jwp-chess/pull/218">Pull Request</a></h2>
<br>

<h2 id="oop-도메인-분리---5">[OOP] 도메인 분리 - 5</h2>
<h3 id="내용">내용</h3>
<ul>
<li>기존 코드는 <strong>도메인</strong> 과 <strong>Spring과 관련된 Repository</strong> 들이 모두 얽혀있었다.</li>
<li>모든 도메인이 Spring에 의존하고 있었다.</li>
<li>테스트도 <strong>@SpringBootTest</strong> 를 사용해 Spring 자체를 실행시켜야 가능했다.</li>
<li>코드 리뷰어 <strong>김고래</strong> 의 피드백을 적용하면서, 도메인은 순수 자바 객체로 유지되어야 함을 깨달았다.</li>
<li>불가피하게 Spring을 띄워야 하는 Repository 관련 테스트를 제외하고, 도메인 관련 테스트들은 모두 순수 자바로만 실행할 수 있게 되었다. 테스트 실행 속도가 매우 빨라졌다.</li>
<li>도메인 자체만 테스트하다보니, 각 객체별로 작은 단위의 테스트를 매우 간단하게 할 수 있었다. 이전에는 모든 의존성을 알맞게 주입해 놓아야 테스트가 가능했다.</li>
<li>콘솔 게임은 Spring 없이 순수하게 자바로만 실행할 수 있게 되었다.</li>
</ul>
<h3 id="태그">태그</h3>
<p>OOP, Domain, Test, Spring</p>
<br>

<h2 id="test-in-memory-db-적용---3">[Test] In-Memory DB 적용 - 3</h2>
<h3 id="내용-1">내용</h3>
<ul>
<li>이전에는 테스트를 실제 DB를 통해서 했었다.</li>
<li>코드 리뷰어 <strong>김고래</strong> 의 피드백으로 Test들은 In-Memory DB인 H2 DB를 사용하게 했다.</li>
<li>테스트가 훨씬 가벼워지고, 외부 DB에 의존하지 않게 되었다. 또한, 속도도 빨라졌다.</li>
</ul>
<h3 id="태그-1">태그</h3>
<p>Test, DB</p>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[Spring MVC Controller 불변객체 매핑]]></title>
            <link>https://velog.io/@taehee-kim-dev/Spring-MVC</link>
            <guid>https://velog.io/@taehee-kim-dev/Spring-MVC</guid>
            <pubDate>Wed, 14 Apr 2021 05:59:35 GMT</pubDate>
            <description><![CDATA[<table>
<thead>
<tr>
<th align="center">-</th>
<th align="center">@ModelAttribute</th>
<th align="center">@RequestBody</th>
<th align="center">@ResponseBody</th>
</tr>
</thead>
<tbody><tr>
<td align="center">변환 종류</td>
<td align="center">GET 쿼리 스트링, POST Body 파라미터 -&gt; DTO</td>
<td align="center">JSON -&gt; DTO</td>
<td align="center">DTO -&gt; JSON</td>
</tr>
<tr>
<td align="center">변환 방식</td>
<td align="center">생성자 초기화</td>
<td align="center">기본 생성자와 getter만 있으면, 필드 다이렉트 주입</td>
<td align="center">Getter 추출</td>
</tr>
</tbody></table>
<br>

<h3 id="생성자-초기화-방식">생성자 초기화 방식</h3>
<ul>
<li><code>요청 GET/POST 파라미터(이름 : 값)</code> -&gt; <code>DTO 생성자 매개변수(이름 : 값)</code></li>
<li>필드의 이름은 데이터 매핑과 관련없다.</li>
<li>생성자 매개변수의 이름이 같아야 한다.</li>
</ul>
<h3 id="기본-생성자와-getter만-있으면-필드-다이렉트-주입-방식">기본 생성자와 getter만 있으면, 필드 다이렉트 주입 방식</h3>
<ul>
<li><code>요청 JSON 파라미터(이름 : 값)</code> -&gt; <code>DTO 필드(이름 : 값)</code></li>
<li>기본 생성자가 반드시 있어야 한다.</li>
<li>getter가 반드시 있어야 한다.</li>
<li>boolean 타입의 경우, 네이밍이 isStart라고 한다면, isIsStart() 라고 명명하면 받아올 수 있다. <strong>(by 우테코 크루 웨지)</strong></li>
</ul>
<h3 id="getter-추출-방식">Getter 추출 방식</h3>
<ul>
<li>반환하는 ResponseDto 객체는 JSON <code>{&quot;${ResponseDto 필드 이름}&quot;: &quot;${ResponseDto 필드 값}&quot;, ... }</code> 형태로 변환되어 반환된다.</li>
<li>Getter가 있어야한다. <ul>
<li>Getter를 통해서 JSON으로 변환된다.</li>
<li><strong>Getter의 이름 기준으로</strong> JSON Data가 생성된다.</li>
<li><code>getAbc()</code> 또는 <code>isAbc()</code> -&gt; <code>{&quot;abc&quot;: &quot;${해당 함수 반환 값}&quot;}</code> 으로 변환된다.</li>
<li>필드의 이름은 데이터 매핑과 관련없다.</li>
</ul>
</li>
</ul>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[우아한테크코스 3기 백엔드 Lv2 [체스 - 1, 2단계] 1차 PR 학습로그]]></title>
            <link>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv2-%EC%B2%B4%EC%8A%A4-1-2%EB%8B%A8%EA%B3%84-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8</link>
            <guid>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv2-%EC%B2%B4%EC%8A%A4-1-2%EB%8B%A8%EA%B3%84-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8</guid>
            <pubDate>Wed, 14 Apr 2021 04:17:45 GMT</pubDate>
            <description><![CDATA[<h2 id="pull-request"><a href="https://github.com/woowacourse/jwp-chess/pull/218">Pull Request</a></h2>
<h1 id="인비의-학습로그">인비의 학습로그</h1>
<hr>
<h2 id="spring-di의존성-주입-적용---4">[Spring] DI(의존성 주입) 적용 - 4</h2>
<h3 id="내용">내용</h3>
<ul>
<li>기존 코드는 객체들이 서로 강하게 결합되어 있었다.</li>
<li>Spring Bean 생성자 주입 방식으로, 모든 객체들 간의 결합도를 낮췄다.</li>
</ul>
<h3 id="태그">태그</h3>
<p>Spring, DI, OOP</p>
<br>

<h2 id="spring-web-mvc-적용---3">[Spring] Web MVC 적용 - 3</h2>
<h3 id="내용-1">내용</h3>
<ul>
<li>Spark Java를 제거하고, Spring Web MVC를 적용했다.</li>
<li>추상화가 잘 되어있어 Controller의 코드가 매우 간결하고 깔끔해졌다.</li>
</ul>
<h3 id="태그-1">태그</h3>
<p>Spring, MVC</p>
<br>

<h2 id="jdbc-jdbctemplate-적용---4">[JDBC] JdbcTemplate 적용 - 4</h2>
<h3 id="내용-2">내용</h3>
<ul>
<li><code>Mysql Jdbc Driver</code> 만 사용해서 작성했던 DAO 클래스들에 <code>JdbcTemplate</code> 를 적용했다.</li>
<li>한 단계 더 추상화 되어있어, 코드가 많이 줄어들고 깔끔해졌다.</li>
<li><code>queryForObject()</code> 로 DB에서 값을 조회할 때, 결괏값의 개수가 0개이면 <code>EmptyResultDataAccessException</code> 가 발생해, <code>try ~ catch</code> 문으로 직접 예외를 처리해야 하는 부분이 아쉬웠다.</li>
</ul>
<h3 id="태그-2">태그</h3>
<p>JDBC</p>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[우아한테크코스 3기 백엔드 Lv1 [체스 - 4, 5단계] Pull Request]]></title>
            <link>https://velog.io/@taehee-kim-dev/%EC%B2%B4%EC%8A%A4-4-5%EB%8B%A8%EA%B3%84-PR</link>
            <guid>https://velog.io/@taehee-kim-dev/%EC%B2%B4%EC%8A%A4-4-5%EB%8B%A8%EA%B3%84-PR</guid>
            <pubDate>Tue, 30 Mar 2021 11:03:10 GMT</pubDate>
            <description><![CDATA[<h2 id="pull-request"><a href="https://github.com/woowacourse/java-chess/pull/216">Pull Request</a></h2>
<h3 id="안녕하세요-제이-smile">안녕하세요, 제이!! :smile:</h3>
<p>이번 미션은 Web View와 DB를 연동하는 미션이라 쉽지 않았습니다.</p>
<p>4단계, 5단계의 필수 요구사항들 외에도, 아래의 선택 요구사항들 까지 모두 구현해 봤습니다.</p>
<pre><code>- 체스 게임방을 만들고 체스 게임방에 입장할 수 있는 기능을 추가한다.
- 사용자별로 체스 게임 기록을 관리할 수 있다.</code></pre><p><strong>사전에</strong> MySQL에서 <code>java-chess/chess_game.sql</code> 파일의 명령문들을 맨 위에서부터 맨 아래까지 순차적으로 실행해야 합니다. 이 과정을 먼저 마쳐야 제출한 애플리케이션을 실행하거나 테스트할 수 있습니다.</p>
<p>그때 제이와 얘기했던 <strong>&quot;<code>Piece</code>와 <code>Board</code>간의 순환적 의존관계&quot;</strong> 를 분리해 봤어요.</p>
<p>특정 <code>Peice</code>가 요청받은 이동 경로로 이동할 수 있는지 판단하는 객체를 따로 만들어, 책임을 분리했습니다.</p>
<p>이를 비롯해 제가 이번 미션에서 고민하고, 학습하고, 적용한 내용들을 아래 학습로그에 정리했어요!</p>
<p>그리고 맨 마지막에 질문도 있습니다!!</p>
<p>답변 해 주시면 정말 감사하겠습니다. :pray:</p>
<p>감사합니다!! :bow: :smile:</p>
<br>

<h1 id="인비의-학습로그">인비의 학습로그</h1>
<h2 id="oop-dto---5">[OOP] DTO - 5</h2>
<h3 id="내용">내용</h3>
<ul>
<li>Domain, Controller, View 간의 데이터 전달에 DTO를 사용했다.</li>
<li>필요한 데이터들을 알맞은 형태로 담아 전달했다.</li>
<li>DTO를 통해 Layer들 간의 결합도를 낮췄다.</li>
</ul>
<h3 id="링크">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-chess/blob/step2/src/main/java/chess/controller/console/dto/CommandRequestDTO.java">CommandRequestDTO</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-chess/tree/step2/src/main/java/chess/controller/dto">다른 DTO들</a></li>
</ul>
<br>

<h2 id="oop-caching---5">[OOP] Caching - 5</h2>
<h3 id="내용-1">내용</h3>
<ul>
<li>DB에 체스 보드 모든 칸들의 위치와 모든 색깔별 기물들을 저장해 두었다.</li>
<li>애플리케이션이 실행될 때 이 위치값들과 색깔별 기물들을 DB로부터 불러와, 객체 형태로 애플리케이션 내에 캐싱했다.</li>
<li>자주 쓰이는 값들을 캐싱해 DB 쿼리 조회 수를 매우 많이 줄여, 성능이 개선됐다.</li>
<li>애플리케이션 내에서 해당 객체들을 사용하기 편했다.</li>
</ul>
<h3 id="링크-1">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-chess/blob/step2/src/main/java/chess/domain/position/cache/PositionsCache.java">PositionsCache</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-chess/blob/step2/src/main/java/chess/domain/piece/cache/PiecesCache.java">PiecesCache</a></li>
</ul>
<br>


<h2 id="oop-점진적-리팩토링---5">[OOP] 점진적 리팩토링 - 5</h2>
<h3 id="내용-2">내용</h3>
<ul>
<li>Web View UI 와 DB를 적용하기 위해 많은 변경이 필요했다.</li>
<li>안전하게 점진적 리팩토링을 했다.</li>
<li>항상 모든 테스트코드들이 통과하도록 했다.</li>
<li>리팩토링에 확신을 갖고 자유롭게 할 수 있었다.</li>
<li>자주 테스트 하고 싶어졌다.</li>
</ul>
<br>


<h2 id="db-query-최적화---5">[DB] Query 최적화 - 5</h2>
<h3 id="내용-3">내용</h3>
<ul>
<li>DB에서 필요한 데이터들만 조회하는 쿼리를 작성했다.</li>
<li>원하는 데이터를 조회하기 위해 쿼리를 여러 번 날리지 않고, 한 방 쿼리로 가져왔다.</li>
<li>요청 쿼리 수가 확연히 줄어 성능이 개선됐다.</li>
</ul>
<h3 id="링크-2">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-chess/blob/step2/src/main/java/chess/dao/PlayerPiecePositionDAO.java">PlayerPiecePositionDAO</a></li>
</ul>
<br>


<h2 id="mvc-의존성-분리---5">[MVC] 의존성 분리 - 5</h2>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/c324ac94-76b7-451e-8233-8be5b7c85b0f/chess_game_classes_dependency_diagram.png" alt=""></p>
<h3 id="내용-4">내용</h3>
<ul>
<li>Controller, Service는 Console용과 Web용이 따로 존재한다.</li>
<li>각 Controller, Service들은 하나의 ChessGame 도메인을 사용한다.</li>
<li>이는 도메인과 모든 Controller, Service들 간의 의존성이 낮다는 것을 의미한다.</li>
<li>다른 종류의 Controller, Service들을 부품 갈아 끼우듯이 자유롭게 교체할 수 있었다.</li>
</ul>
<br>


<h2 id="oop-domain-의존성-분리---5">[OOP] Domain 의존성 분리 - 5</h2>
<h3 id="내용-5">내용</h3>
<ul>
<li>이전에는 <code>Board</code>가 <code>Piece</code>를 알고, <code>Piece</code>가 <code>Board</code>를 알고 있었다.</li>
<li>이는 서로 의존성이 높음을 의미한다.</li>
<li>기물 이동 요청에 대해 이동이 가능한지 판단하는 책임을  <code>MoveChecker</code> 객체로 완전히 분리했다.</li>
<li><code>Board</code>와 <code>Piece</code>간의 의존성을 현저히 낮출 수 있었다.</li>
<li>이전에는 플레이어의 점수 계산을 <code>Pieces</code> 도메인에서 했다.</li>
<li>점수 계산 책임을 <code>ScoreCalculator</code> 객체로 분리했다.</li>
</ul>
<h3 id="링크-3">링크</h3>
<ul>
<li><a href="https://github.com/taehee-kim-dev/java-chess/blob/step2/src/main/java/chess/domain/board/move/MoveChecker.java">MoveChecker</a></li>
<li><a href="https://github.com/taehee-kim-dev/java-chess/blob/step2/src/main/java/chess/domain/player/score/ScoreCalculator.java">ScoreCalculator</a></li>
</ul>
<br>


<h2 id="db-테이블-연관관계-매핑---5">[DB] 테이블 연관관계 매핑 - 5</h2>
<p><img src="https://images.velog.io/images/taehee-kim-dev/post/21925828-b327-44e8-bb05-c240073c06c3/ERD_%EC%BA%A1%EC%B3%90.PNG" alt=""></p>
<h3 id="내용-6">내용</h3>
<ul>
<li>인프런에서 김영한 님의 JPA 강좌에서 공부했던 내용들을 기반으로 DB 테이블 연관관계를 매핑했다.</li>
<li>N : 1 (다대일) 관계를 실무에서 제일 많이 쓰고, N : M (다대다) 관계는 중간에 N : 1 (다대일), 1 : M (다대일) 관계의 중간 테이블을 만들어 풀어내야 한다고 하셨다. 그래야 매핑 관리에 유리하고, 각 매핑에 대한 추가적인 정보를 중간 매핑 테이블에 저장할 수 있기 때문이다.</li>
<li>김영한 님의 강의 내용을 바탕으로 위와 같이 테이블들을 매핑했다.</li>
</ul>
<br>

<h2 id="질문">질문</h2>
<ol>
<li><p>Entity, Domain, Domain Model에 대해 궁금합니다.</p>
<ol>
<li><p>Entity는 DB의 테이블과 직접적으로 맞닿아 있는 객체라고 알고 있습니다. 굳이 이해가 쉽도록 표현하자면, <strong>&quot;DB와 Domain 사이의 DTO같은 느낌??&quot;</strong>. 그렇다면, Entity는 Domain 인가요? 아닌가요? 상황에 따라 다를까요? 보는 관점에 따라 다를까요? 헷갈리네요 :sob:. 사실 JPA를 보면, 모든 Entity 클래스를 Domain으로 보는 것 같기도 해요. 결국 개발 대상의 클래스니까요. 구분 기준을 어떻게 두어야 할까요? </p>
</li>
<li><p>Domain Model은 뭘까요? ERD처럼 Domain들에 대한 설계? <strong>&quot;Domain들에 대한 클래스 다이어그램&quot;</strong> 인가요?? 구글링을 해봐도 잘 모르겠습니다. :cry:</p>
</li>
</ol>
</li>
</ol>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[TDD와 리팩토링, 인터페이스]]></title>
            <link>https://velog.io/@taehee-kim-dev/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%8B%9C-TDD</link>
            <guid>https://velog.io/@taehee-kim-dev/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%8B%9C-TDD</guid>
            <pubDate>Wed, 24 Mar 2021 09:05:26 GMT</pubDate>
            <description><![CDATA[<h2 id="처음-기능-구현-시">처음 기능 구현 시</h2>
<ol>
<li><p>프로그래밍 요구사항에 대한 기능 구현 목록을 잘 작성한다.</p>
</li>
<li><p>기능 구현 목록 = 객체들이 가져야 할 책임들.</p>
</li>
<li><p>책임을 어울리는 객체들에게 나눠준다.</p>
</li>
<li><p>책임들에 대한 테스트 코드들을 꼼꼼하게 작성한다.</p>
</li>
<li><p>테스트들이 모두 통과하도록 프로덕션 코드를 구현한다.</p>
</li>
</ol>
<h2 id="리팩토링-시">리팩토링 시</h2>
<ol>
<li><p>프로그래밍 요구사항 == 기능 구현 목록 == 객체들이 가져야 할 책임들 -&gt; 테스트 코드로 작성되어 있음.</p>
</li>
<li><p>리팩토링 시, 책임들이 객체간에 이동할 순 있어도, 바뀔 순 없다. 왜냐하면, 책임들은 프로그래밍 요구사항이기 때문이다.</p>
</li>
<li><p>모든 기능(책임)들을 인터페이스로 구현한다. -&gt; 구현은 신경쓰지 않는다 == 구현 객체 내부의 수정과, 구현 객체 자체의 교체가 용이하도록 한다.</p>
</li>
<li><p>테스트 코드를 수정할 필요가 없다.</p>
</li>
</ol>
<br>

<h3 id="결론-">결론 :</h3>
<h3 id="모든-기능책임프로그래밍-요구사항들을-인터페이스로-추상화-한다-인터페이스들의-모든-함수들을-테스트-코드로-작성한다">모든 기능(책임)(프로그래밍 요구사항)들을 인터페이스로 추상화 한다. 인터페이스들의 모든 함수들을 테스트 코드로 작성한다.</h3>
<h3 id="모든-테스트들이-통과하면-모든-기능책임프로그래밍-요구사항들을-구현-완료한-것이다">모든 테스트들이 통과하면, 모든 기능(책임)(프로그래밍 요구사항)들을 구현 완료한 것이다.</h3>
<h3 id="인터페이스-구현-객체-내부의-수정과-구현-객체-자체의-교체가-자유롭다">인터페이스 구현 객체 내부의 수정과, 구현 객체 자체의 교체가 자유롭다.</h3>
<h3 id="프로그래밍-요구사항이-변경되지-않는-한-리팩토링-시-테스트-코드를-수정할-필요가-없다">프로그래밍 요구사항이 변경되지 않는 한, 리팩토링 시 테스트 코드를 수정할 필요가 없다.</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[우아한테크코스 3기 백엔드 Lv1 [체스 - 1, 2, 3단계] Pull Request]]></title>
            <link>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv1-%EC%B2%B4%EC%8A%A4-1%EB%8B%A8%EA%B3%84-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8</link>
            <guid>https://velog.io/@taehee-kim-dev/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-3%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-Lv1-%EC%B2%B4%EC%8A%A4-1%EB%8B%A8%EA%B3%84-%ED%95%99%EC%8A%B5%EB%A1%9C%EA%B7%B8</guid>
            <pubDate>Mon, 22 Mar 2021 03:00:52 GMT</pubDate>
            <description><![CDATA[<h3 id="pull-request"><a href="https://github.com/woowacourse/java-chess/pull/176">Pull Request</a></h3>
<h3 id="안녕하세요-제이-😃">안녕하세요, 제이!! 😃</h3>
<p>반갑습니다!!</p>
<p>체스 규칙이 복잡해 구현이 어려웠던 미션이었습니다. :sweat_smile:</p>
<p>Pawn의 규칙이 가장 까다로웠습니다.</p>
<p>구조가 복잡해지다 보니, 각 객체들의 책임에 대해 고민을 많이 했어요.</p>
<p>체스 보드 세팅 정보를 외부에서 주입하도록 해, 매우 직관적이고 테스트에 용이한 코드를 작성했습니다.</p>
<p>로직들이 복잡하다 보니, 메서드들이 많아져 고민을 많이 했어요.</p>
<p>체스 보드 칸 위치와 기물들을 캐싱했습니다.</p>
<p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/type/Direction.java">Direction</a> 에서 방향을 판단하고, 각 기물의 이동 가능 방향들을 일일이 반환해 주다 보니, 클래스에 함수가 많아졌네요. :sob:</p>
<p>어려운 만큼 많이 성장하고 있는게 느껴져요!! :muscle:</p>
<p>제이의 피드백을 통해 더 성장하고 싶습니다. :smile:</p>
<p>감사합니다!! :bow:</p>
<br>

<h1 id="인비의-학습로그">인비의 학습로그</h1>
<h2 id="oop-추상클래스---5">[OOP] 추상클래스 - 5</h2>
<h3 id="내용">내용</h3>
<ul>
<li>도메인들의 공통 부분들을 추상클래스로 뽑아 추상화 했다. </li>
<li>중복이 사라지고, 자식클래스들이 매우 간단해졌다.</li>
<li>메서드 오버라이딩을 통한 다형성을 구현했다.</li>
</ul>
<h3 id="링크">링크</h3>
<ul>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/Piece.java">Piece</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/board/setting/BoardSetting.java">BoardSetting</a></p>
<br>

</li>
</ul>
<h2 id="oop-dto---2">[OOP] DTO - 2</h2>
<h3 id="내용-1">내용</h3>
<ul>
<li><p>Controller와 View의 의존성을 분리하기 위해 DTO를 사용했다.</p>
</li>
<li><p>View에 필요한 데이터들을 알맞게 담아 넘겨줄 수 있었다.</p>
<h3 id="링크-1">링크</h3>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/controller/dto/CommandDTO.java">CommandDTO</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/controller/dto/ScoresDTO.java">ScoresDTO</a></p>
</li>
</ul>
<br>

<h2 id="oop-의존성-주입---4">[OOP] 의존성 주입 - 4</h2>
<h3 id="내용-2">내용</h3>
<ul>
<li><p>체스 보드 세팅을 외부에서 주입하도록 했다.</p>
</li>
<li><p>테스트 코드에서 Custom한 기물 배치를 할 수 있도록 해, 테스트에 용이한 구조를 만들었다.</p>
<h3 id="링크-2">링크</h3>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/board/setting/BoardSetting.java">BoardSetting</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/board/setting/BoardDefaultSetting.java">BoardDefaultSetting</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/board/setting/BoardCustomSetting.java">BoardCustomSetting</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/test/java/chess/domain/piece/PieceTest.java">PieceTest</a></p>
</li>
</ul>
<br>

<h2 id="java-enum---5">[Java] Enum - 5</h2>
<h3 id="내용-3">내용</h3>
<ul>
<li><p>Enum 클래스로 상수들을 종류별로 묶어서 관리했다.</p>
</li>
<li><p>상수들을 종류별로 편리하게 관리하고 사용할 수 있었다.</p>
</li>
<li><p>한 Enum 클래스에서 다른 Enum 클래스의 상수들을 묶어 활용하는 것을 경험할 수 있었다.</p>
<h3 id="링크-3">링크</h3>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/board/type/File.java">File</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/board/type/Rank.java">Rank</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/game/type/Command.java">Command</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/type/Direction.java">Direction</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/type/PieceType.java">PieceType</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/type/PieceWithColorType.java">PieceWithColorType</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/player/type/TeamColor.java">TeamColor</a></p>
</li>
</ul>
<br>

<h2 id="oop-일급-컬렉션---3">[OOP] 일급 컬렉션 - 3</h2>
<h3 id="내용-4">내용</h3>
<ul>
<li><p>컬렉션을 클래스로 포장해 일급 컬렉션으로 만들었다.</p>
</li>
<li><p>함수를 통해 컬렉션 활용을 다양하게 할 수 있었다.</p>
</li>
<li><p>캡슐화를 하여 컬렉션의 남용을 방지했다.</p>
<h3 id="링크-4">링크</h3>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/board/Board.java">Board</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/position/cache/PositionsCache.java">PositionsCache</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/cache/PiecesCache.java">PiecesCache</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/player/Players.java">Players</a></p>
</li>
</ul>
<br>

<h2 id="oop-caching---4">[OOP] Caching - 4</h2>
<h3 id="내용-5">내용</h3>
<ul>
<li><p>체스 보드 칸의 위치들과 기물들을 캐싱했다.</p>
</li>
<li><p>객체를 새로 생성하지 않고 재사용해, 메모리를 절약할 수 있었다.</p>
</li>
<li><p>static 메서드를 사용해 객체를 편리하게 가져올 수 있었다.</p>
<h3 id="링크-5">링크</h3>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/position/cache/PositionsCache.java">PositionsCache</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/cache/PiecesCache.java">PiecesCache</a></p>
</li>
</ul>
<br>

<h2 id="junit-nested-어노테이션---5">[JUnit] Nested 어노테이션 - 5</h2>
<h3 id="내용-6">내용</h3>
<ul>
<li><p>동일한 맥락의 테스트 코드들을 Nested 어노테이션을 사용해 묶었다.</p>
</li>
<li><p>Nested 어노테이션을 사용하지 않을 때는 연관된 테스트들끼리 구별하기가 어려웠는데, 이를 사용하니 훨씬 편해졌다.</p>
</li>
<li><p>Inner 클래스의 장점들을 활용할 수 있었다.</p>
<h3 id="링크-6">링크</h3>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/test/java/chess/domain/game/ChessGameTest.java">ChessGameTest</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/test/java/chess/domain/piece/PieceTest.java">PieceTest</a></p>
</li>
</ul>
<br>

<h2 id="test-테스트하기-쉬운-코드---5">[Test] 테스트하기 쉬운 코드 - 5</h2>
<h3 id="내용-7">내용</h3>
<ul>
<li><p>처음에는 테스트 코드를 체스 보드의 위치값 문자열로만 테스트해, 작성하기도 어렵고 이해하기도 어려웠다.</p>
</li>
<li><p>테스트하기 쉬운 코드로 리팩토링 하고 나니, 테스트 코드가 직관적이 되어, 이해하기도 작성하기도 쉬워졌다.</p>
<h3 id="링크-7">링크</h3>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/test/java/chess/domain/game/ChessGameTest.java">ChessGameTest</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/test/java/chess/domain/piece/PieceTest.java">PieceTest</a></p>
</li>
</ul>
<br>

<h2 id="java-stream---3">[Java] Stream - 3</h2>
<h3 id="내용-8">내용</h3>
<ul>
<li><p>Stream 을 적극 활용하여, 컬렉션을 자유롭게 활용했다.</p>
</li>
<li><p>함수의 개수와 인덴트를 효과적으로 줄일 수 있었다.</p>
<h3 id="링크-8">링크</h3>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/board/type/File.java">File</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/board/type/Rank.java">Rank</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/board/Board.java">Board</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/game/type/Command.java">Command</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/cache/PiecesCache.java">PiecesCache</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/type/Direction.java">Direction</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/piece/Pieces.java">Pieces</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/player/Players.java">Players</a></p>
</li>
<li><p><a href="https://github.com/taehee-kim-dev/java-chess/blob/step1/src/main/java/chess/domain/position/cache/PositionsCache.java">PositionsCache</a></p>
</li>
</ul>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[학습 로그 말하기 회고 - 2021.03.11(목)]]></title>
            <link>https://velog.io/@taehee-kim-dev/%ED%95%99%EC%8A%B5-%EB%A1%9C%EA%B7%B8-%EB%A7%90%ED%95%98%EA%B8%B0-%ED%9A%8C%EA%B3%A0-2021.03.11%EB%AA%A9</link>
            <guid>https://velog.io/@taehee-kim-dev/%ED%95%99%EC%8A%B5-%EB%A1%9C%EA%B7%B8-%EB%A7%90%ED%95%98%EA%B8%B0-%ED%9A%8C%EA%B3%A0-2021.03.11%EB%AA%A9</guid>
            <pubDate>Thu, 11 Mar 2021 04:32:15 GMT</pubDate>
            <description><![CDATA[<p>시드, 알리와 학습로그 말하기를 했다.</p>
<p>현구막과 했을 때 말하기 시간이 길어졌던 문제점이 기억났다.</p>
<p>스톱워치를 켜고 각자 학습로그를 설명할 때 얼마나 걸리는지 쟀다.</p>
<p>두괄식으로 명확하고 간략하게 말하려고 노력했다.</p>
<p>이를 의식하고 말하려고 하니 약간 더듬거렸다.</p>
<p>계속 말하기 훈련을 하다 보면, 더듬거림이 줄어들지 않을까?</p>
<p>알았던 내용이라도, 각자 적용 방법들이 달라 새로운 인사이트를 얻을 수 있었다.</p>
<p>몰랐던 내용은, 학습하고 싶은 흥미가 생겼다.</p>
<p>알리가 Collectors API의 groupBy 사용 경험에 대해 말했다.</p>
<p>나는 몰랐던 내용이라 흥미가 생겨, 공부해보려고 한다.</p>
]]></description>
        </item>
    </channel>
</rss>