<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>now_iz.log</title>
        <link>https://velog.io/</link>
        <description>👀</description>
        <lastBuildDate>Mon, 06 Sep 2021 12:30:58 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>now_iz.log</title>
            <url>https://images.velog.io/images/now_iz/profile/1eefad2c-486d-4e4a-a8c3-78d7a43eee0d/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. now_iz.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/now_iz" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[빌드와 배포]]></title>
            <link>https://velog.io/@now_iz/%EB%B9%8C%EB%93%9C%EC%99%80-%EB%B0%B0%ED%8F%AC</link>
            <guid>https://velog.io/@now_iz/%EB%B9%8C%EB%93%9C%EC%99%80-%EB%B0%B0%ED%8F%AC</guid>
            <pubDate>Mon, 06 Sep 2021 12:30:58 GMT</pubDate>
            <description><![CDATA[<h1 id="빌드">빌드</h1>
<h2 id="build">Build</h2>
<ul>
<li>소스 코드 파일을 <strong>컴퓨터에서 실행할 수 있는 독립 소프트웨어 가공물로 변환</strong>하는 일련의 과정이나 결과물이다.<ul>
<li>컴파일, 링크, Resource 등 실행에 필요한 파일을 지정된 위치로 옮기는 행위, 테스팅, JAR 혹은 WAR로의 패키징 등</li>
<li>컴파일과 링크 등은 빌드의 여러 단계 중 한 가지에 속하는 부분 집합이다.<h2 id="build-tool">Build Tool</h2>
</li>
</ul>
</li>
<li>상기한 빌드 프로세스를 자동으로 수행함으로써 <strong>소스 코드를 실행 가능한 어플리케이션으로 생성해주는 도구</strong>이다.</li>
<li>프로젝트 규모가 확대될수록 사용하는 라이브러리가 많아진다.</li>
<li>라이브러리를 직접 다운받아서 추가하는 등 수동 작업이 매우 번거롭고 실수가 발생하기 쉽다.</li>
<li>빌드 툴은 라이브러리를 자동으로 추가 및 관리해준다.</li>
<li>라이브러리의 버전을 자동으로 동기화하는 기능도 제공한다.</li>
<li>ex) Gradle, Maven</li>
</ul>
<h1 id="무중단-배포">무중단 배포</h1>
<p>새로운 빌드 결과물을 서버의 다운타임 없이 배포하는 &#39;무중단 배포&#39; 전략에 대해서 살펴보겠다!</p>
<h2 id="롤링-rolling">롤링 (Rolling)</h2>
<p><img src="https://images.velog.io/images/now_iz/post/adfd5b2f-8881-4f14-96ce-26afd8f8a817/image.png" alt=""></p>
<ul>
<li>원리<ul>
<li>서버 1을 로드 밸런서에서 뺀다.</li>
<li>서버 1을 배포한다.</li>
<li>서버 1을 다시 로드 밸런서에 넣는다.</li>
<li>서버 2를 로드 밸런서에서 뺀다.</li>
<li>서버 2에 배포한다.</li>
<li>서버 2를 다시 로드 밸런서에 넣는다.</li>
<li>(모든 서버가 배포 완료 될 때 까지 계속 진행)</li>
</ul>
</li>
<li>단점<ul>
<li>배포할 서버가 너무 많다면 n대 단위로 배포하는데, 배포가 모두 끝나기 전까지는 누구는 이전 서비스를 받고 누구는 신규 서비스를 받는 문제가 존재한다.</li>
<li>1대에 배포하는 것 보다 최소 2배 이상 느리다.</li>
</ul>
</li>
</ul>
<h2 id="카나리canary">카나리(Canary)</h2>
<p><img src="https://images.velog.io/images/now_iz/post/efdc76b4-06a5-4fe7-a697-40a3bc560322/image.png" alt="">
소수의 유저만 사용하는 환경에 신규 버전을 배포하고, 문제가 없다고 판단됐을 때 다른 모든 서버에 배포하는 방식이다.
트래픽을 분산시킬 때에는 라우팅을 랜덤하게 할 수도 있고, 사용자를 분류할 수도 있다.</p>
<ul>
<li>장점<ul>
<li>위험을 빠르게 감지할 수 있다.</li>
<li>A/B 테스트가 가능하고, 성능 모니터링에 유용하다.</li>
</ul>
</li>
</ul>
<h2 id="블루-그린-blue-green">블루 그린 (blue green)</h2>
<p><img src="https://images.velog.io/images/now_iz/post/8ef66201-2d47-46ba-ad0b-6428f850fc0f/image.png" alt="">
실제로 서비스 중인 환경(Blue)과 <strong>새롭게 배포할 환경(Green)</strong>을 세트로 준비해서 배포하는 방식이다.</p>
<ul>
<li>장점<ul>
<li>새롭게 배포할 환경에만 (동시에) 배포하면 되기 때문에 <strong>배포 속도가 매우 빠르다.</strong></li>
<li>언제나 Green 환경이 떠있기 떄문에, 만약 잘못된 버전으로 배포한 경우 <strong>신속하게 롤백할 수 있다.</strong></li>
</ul>
</li>
<li>단점<ul>
<li>Green 환경이 항상 떠있어야 하기 때문에 <strong>비용이 2배로 든다.</strong></li>
</ul>
</li>
</ul>
<h1 id="reference">reference</h1>
<ul>
<li><a href="https://xlffm3.github.io/devops/build-deploy-ci-cd/">https://xlffm3.github.io/devops/build-deploy-ci-cd/</a></li>
<li><a href="https://reference-m1.tistory.com/211">https://reference-m1.tistory.com/211</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JVM 과 GC]]></title>
            <link>https://velog.io/@now_iz/JVM-%EA%B3%BC-GC</link>
            <guid>https://velog.io/@now_iz/JVM-%EA%B3%BC-GC</guid>
            <pubDate>Tue, 24 Aug 2021 10:55:00 GMT</pubDate>
            <description><![CDATA[<h1 id="jvm-runtime-data-area">JVM Runtime Data Area</h1>
<h2 id="heap-영역">Heap 영역</h2>
<ul>
<li>인스턴스, 배열 등을 저장</li>
<li>JDK 8 부터는 Perm 영역이 사라졌다.<h3 id="young-영역---마이너-gc">Young 영역 - 마이너 GC</h3>
</li>
<li>처음 객체가 생성되면, <code>Eden</code> 영역에 객체가 지정된다. <code>Eden</code> 영역이 꽉차면, GC 를 통해서 삭제되거나 살아남은 경우에는 <code>Survivor</code> 영역으로 옮겨진다. <code>Survivor</code> 영역은 2개로 나뉘어 있는데, 둘 중 하나는 반드시 비어 있어야 한다. 이런 작업을 반복하다가 <code>Old</code> 영역으로 이동하게 된다. (단, <code>Young</code> 영역에서 바로 <code>Old</code> 영역으로 이동하는 경우도 있는데, 객체의 크기가 아주 커서 <code>Survivor</code> 영역 보다 큰 경우다.)
이렇게 <code>Young</code> 영역에서 발생하는 GC를 <code>마이너 GC</code> 라고 한다. <h3 id="old-영역---메이저-gc">Old 영역 - 메이저 GC</h3>
</li>
<li><code>Old</code> 영역이나 <code>Perm</code> 영역에서 발생하는 GC 를 <code>메이저 GC</code> 라고 한다.</li>
</ul>
</br>
</br>

<h1 id="gc">GC</h1>
<ul>
<li>하나의 객체는 메모리(힙)를 점유하는데, 필요하지 않을 때 메모리(힙)에서 객체를 해제한다.</li>
<li>GC 는 메모리를 할당하고, 사용 중이거나 사용 중이지 않은 메모리를 인식하는 역할을 한다.<blockquote>
<h4 id="outofmemoryerror">OutOfMemoryError</h4>
<p>더 이상 사용 가능한 메모리 영역이 없는데, 계속 메모리를 할당하려고 할 때 <code>OutOfMemoryError</code> 이 발생하여 JVM 이 다운될 수 있다. </p>
</blockquote>
</li>
</ul>
<h2 id="방식">방식</h2>
<ul>
<li>Serial Collector<ul>
<li>Young 영역과 <code>Old</code> 영역을 연속적으로 처리하며, 하나의 CPU 를 사용한다. </li>
<li>대기 시간이 많아도 크게 문제가 되지 않는 클라이언트 장비에서 주로 사용한다.</li>
<li><code>stop-the world</code></li>
<li><code>mark-sweep-compact</code> : <code>Old</code> 영역으로 이동된 객체들 중 살아 있는 객체와 쓰레기 객체를 식별한다. 쓰레기 객체를 삭제하고, 살아 있는 객체를 한 곳으로 모은다.<blockquote>
<h4 id="mark-sweep-compact">Mark-Sweep-Compact</h4>
</blockquote>
</li>
<li></li>
</ul>
</li>
<li>Parallel Collector (= Throughput Collector)<ul>
<li>다른 CPU 가 대기 상태로 남아있는 것을 최소화한다.</li>
<li><code>Young</code> 영역에서는 (Serial Collector 와 달리) 병렬적으로 처리한다.</li>
<li>많은 CPU 를 사용하기 때문에, CG 의 부하를 줄이고, 애플리케이션 처리량을 늘릴 수 있다.</li>
<li><code>Old</code> 영역에서는 <code>mark-sweep-compact</code> 를 사용한다.</li>
</ul>
</li>
<li>Parallel Compacting Collector<ul>
<li>여러 CPU 를 사용하는 서버에 적합하다.</li>
</ul>
</li>
<li>G1 Collector<ul>
<li>바둑판 모양의 구역으로 메모리를 나누고, 각각 <code>Eden</code>, <code>Survivor</code>, <code>Old</code> 역할을 지정한다.<h2 id="성능">성능</h2>
</li>
</ul>
</li>
<li><code>full GC</code> 가 실행 중일 때에는 JVM 에서 처리되지 않는다. 따라서, GC 를 많이 할수록 응답 시간에 영향을 끼치게 된다.</li>
<li>자바는 메모리를 GC 를 통해서 관리하기 때문에, 메모리 처리 로직을 굳이 만들 필요가 없다.<ul>
<li>사실, 절대로 GC 를 쓰지 않는 것을 권장한다. <code>System.gc()</code> 등의 메소드로 </li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Redis 로 캐싱하기]]></title>
            <link>https://velog.io/@now_iz/Redis-%EB%A1%9C-%EC%BA%90%EC%8B%B1%ED%95%98%EA%B8%B0-3m5hibt0</link>
            <guid>https://velog.io/@now_iz/Redis-%EB%A1%9C-%EC%BA%90%EC%8B%B1%ED%95%98%EA%B8%B0-3m5hibt0</guid>
            <pubDate>Wed, 11 Aug 2021 18:15:44 GMT</pubDate>
            <description><![CDATA[<h1 id="rediscacheconfigjava">RedisCacheConfig.java</h1>
<pre><code class="language-Java">@EnableCaching
@Configuration
public class RedisCacheConfig {

    @Value(&quot;${spring.redis.cache.host}&quot;)
    private String host;

    @Value(&quot;${spring.redis.cache.port}&quot;)
    private int port;

    @Value(&quot;${spring.redis.cache.password}&quot;)
    private String password;

    @Bean
    public RedisConnectionFactory redisCacheConnectionFactory() {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName(host);
        config.setPort(port);
        config.setPassword(password);
        return new LettuceConnectionFactory(config);
    }

    @Bean
    public RedisTemplate&lt;String, Object&gt; redisTemplate() {
        RedisTemplate&lt;String, Object&gt; redisTemplate = new RedisTemplate&lt;&gt;();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(redisCacheConnectionFactory());
        return redisTemplate;
    }

    @Bean
    public RedisCacheConfiguration defaultRedisCacheConfiguration() {
        return RedisCacheConfiguration.defaultCacheConfig()
                .disableCachingNullValues()
                .entryTtl(Duration.ofHours(1L))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
    }

    @Bean
    public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
        return (builder) -&gt; builder
                .withCacheConfiguration(&quot;products&quot;, defaultRedisCacheConfiguration().entryTtl(Duration.ofMinutes(30)));
    }
}</code></pre>
<h3 id="enablecaching"><code>@EnableCaching</code></h3>
<ul>
<li>스프링에 AOP로 구현되어있는 캐시 로직을 사용한다.</li>
<li>스프링이 <code>CacheManager</code> 인터페이스를 추상화하였기 때문에 <code>RedisCacheManager</code>, <code>EhCacheManager</code> 등 필요한 <code>CacheManage</code> 로 갈아끼워 사용할 수 있다.</li>
</ul>
<h3 id="캐시-설정">캐시 설정</h3>
<ul>
<li><code>CacheManager</code> 를 이용해서 Serializer, TTL(Time to Live), key 의 prefix 등을 디테일하게 설정할 수 있다.</li>
<li><code>RedisCacheManagerBuilderCustomizer</code> 를 사용하여 캐시마다 TTL 등 캐시 정책을 다르게 적용할 수 있도록 하였다.</li>
</ul>
<h1 id="productcontrollerjava">ProductController.java</h1>
<pre><code class="language-Java">@RequestMapping(&quot;/products&quot;)
@RequiredArgsConstructor
@RestController
public class ProductController {

    private final ProductService productService;

    @Cacheable(key=&quot;#dto.start&quot;, value=&quot;products&quot;)
    @GetMapping
    public SuccessResponse getProducts(@Valid @ModelAttribute GetProductsRequest dto) {
        List&lt;SimpleProduct&gt; products = productService.getProducts(dto);
        return SuccessResponse.builder()
                .status(StatusEnum.OK)
                .message(&quot;상품 목록 가져오기 성공&quot;)
                .data(products)
                .build();
    }
 }</code></pre>
</br>
</br>

<h3 id="캐시-저장삭제">캐시 저장/삭제</h3>
<ul>
<li>캐시 저장<ul>
<li><code>@Cacheable</code> : 캐시가 있으면 캐시의 정보를 가져오고, 없으면 등록한다.<ul>
<li>SpEL(Spring Expression Language) 을 지원한다.</li>
<li><code>@Cacheable(key=&quot;#dto.start&quot;, value=&quot;products&quot;)</code><ul>
<li>(필자는 사용하지 않았으나) <code>unless</code> 라는 옵션으로 조건을 걸어줄 수 있으니 참고하기 바란다!</li>
</ul>
</li>
</ul>
</li>
<li><code>@CachePut</code> : 무조건 캐시에 저장한다.</li>
</ul>
</li>
<li>캐시 삭제<ul>
<li><code>@CacheEvict</code> : 캐시 데이터와 DB의 불일치성이 발생할 수 있기 때문에 캐시 데이터의 update, delete가 발생할 경우 해당 어노테이션을 적용하여 캐싱 데이터를 제거해야 한다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring 과 정적 리소스]]></title>
            <link>https://velog.io/@now_iz/Spring-%EA%B3%BC-%EC%A0%95%EC%A0%81-%EB%A6%AC%EC%86%8C%EC%8A%A4</link>
            <guid>https://velog.io/@now_iz/Spring-%EA%B3%BC-%EC%A0%95%EC%A0%81-%EB%A6%AC%EC%86%8C%EC%8A%A4</guid>
            <pubDate>Fri, 06 Aug 2021 19:02:52 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<pre><code class="language-Java">@Component
@RequiredArgsConstructor
public class AuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws UnauthorizedException {
        try {
            if (isNeedToAuth((HandlerMethod)handler)) {
                String userIdBySession = getUserIdBySession(request);
                String userIdByPath = getUserIdByPathVariable(request);
                if (!userIdBySession.equals(userIdByPath)) {
                    throw new UnauthorizedException();
                };
            }
            return true;
        } catch (Exception e) {
            throw new UnauthorizedException(e);
        }
    }
    // 기타 메소드 생략
}</code></pre>
<pre><code>@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private AuthInterceptor authInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(authInterceptor);
    }
}</code></pre><p>대략 <code>@AuthRequired</code> 어노테이션이 붙은 MethodHandler 에 로그인 세션 검증을 추가하는 인터셉터를 만들었다!
그런데 문제는 일반적으로 404 에러가 발생해야 하는 &quot;서비스하지 않는 URL&quot;을 요청했을 때,
<code>ResourceHttpRequestHandler</code> 를 <code>HandlerMethod</code> 로 타입변환 할 수 없다는 에러가 발생한다. 
별다른 설정이 없는 한, 서버 에러를 띄우게 된다!</p>
<blockquote>
<p><strong>java.lang.ClassCastException:</strong> 
class org.springframework.web.servlet.resource.<strong>ResourceHttpRequestHandler cannot be cast to</strong> class org.springframework.web.method.<strong>HandlerMethod</strong> (org.springframework.web.servlet.resource.ResourceHttpRequestHandler and org.springframework.web.method.HandlerMethod are in unnamed module of loader &#39;app&#39;)</p>
</blockquote>
</br>
</br>

<h1 id="정적-리소스-요청">정적 리소스 요청</h1>
<p>스프링 2.X 프레임워크에서 정적 리소스를 요청할 때에도 인터셉터가 호출되기 때문에,
인터셉터에 전달되는 핸들러가 모두 HandlerMethod가 아닐 수 있다는 것이다!</p>
<p>API 로 등록되지 않은 모든 URL을 정적 리소스를 요청하는 URL 로 바꾸는 것은 성능 향상을 위해 2.X 부터 바꾼 것으로 추측해본다.</p>
<blockquote>
<h4 id="정적-리소스">정적 리소스</h4>
</blockquote>
<ul>
<li>클라이언트로부터 요청이 들어왔을 때, 요청에 대한 리소스가 이미 만들어져 있어 그대로 응답하는 경우</li>
<li>html, css, js, image 등</li>
</ul>
<h3 id="resourcehttprequesthandler">ResourceHttpRequestHandler</h3>
<ul>
<li>아까 우리가 <code>HandlerMethod</code> 로 들어올 것이라고 예상했으나, 실제로 받았던 이 핸들러가 바로 &#39;정적 리소스를 처리하는 핸들러&#39; 이다!</li>
<li>브라우저가 최초로 정적리소스를 요청하는 경우에는 응답으로, <code>200</code> 응답 코드에, <code>Last-modified</code> 헤더를 함께 보낸다.</li>
<li>브라우저는 이 정보를 기억하고 있다가, 다음 요청에서는 <code>If-Modified-Since</code> 헤더를 포함해서 보낸다.<ul>
<li>서버는<code>If-Modified-Since</code> 헤더값과 리소스의 변경 시점이 같다면<ul>
<li><code>304</code> 상태코드를 응답하면서 다시 리소스를 응답하지 않는다.</li>
</ul>
</li>
<li>서버는 <code>If-Modified-Since</code> 헤더값과 리소스의 변경 시점이 다르다면<ul>
<li>변경된 리소스와 <code>200</code> 상태코드를 응답하게 되면서 브라우저는 다시 응답의 <code>Last-Modified</code> 헤더값을 다음 요청의 <code>If-Modified-Since</code> 헤더값에 포함하여 전송한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
</br>
</br>

<h1 id="해결">해결</h1>
<pre><code class="language-Java">@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private AuthInterceptor authInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(authInterceptor)
            .addPathPatterns(&quot;/rest-api-root/**&quot;);;
    }
}</code></pre>
<p>이렇게 <code>addPathPatterns</code> 를 이용해서, REST API 의 루트 URL 을 명시해주면, 정적 요청없이 잘 동작한다!</p>
</br>
</br>


<h1 id="reference">Reference</h1>
<ul>
<li><a href="https://cheolhojung.github.io/posts/java/web-mvc-static-resources.html">https://cheolhojung.github.io/posts/java/web-mvc-static-resources.html</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[확장 가능한 글로벌 서비스 만들기 - (1) 타임존 관리]]></title>
            <link>https://velog.io/@now_iz/%ED%99%95%EC%9E%A5-%EA%B0%80%EB%8A%A5%ED%95%9C-%EA%B8%80%EB%A1%9C%EB%B2%8C-%EC%84%9C%EB%B9%84%EC%8A%A4-%EB%A7%8C%EB%93%A4%EA%B8%B0-1-%ED%83%80%EC%9E%84%EC%A1%B4-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@now_iz/%ED%99%95%EC%9E%A5-%EA%B0%80%EB%8A%A5%ED%95%9C-%EA%B8%80%EB%A1%9C%EB%B2%8C-%EC%84%9C%EB%B9%84%EC%8A%A4-%EB%A7%8C%EB%93%A4%EA%B8%B0-1-%ED%83%80%EC%9E%84%EC%A1%B4-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Sat, 31 Jul 2021 12:58:31 GMT</pubDate>
            <description><![CDATA[<h1 id="글로벌-서비스">글로벌 서비스</h1>
<p>우리 서비스는 언젠가 전세계에서 이용 가능한 <strong>글로벌 이커머스 서비스</strong>가 될 수 있다는 가정 하에, 어떻게 설계할지 고민했다 🧐
여러 국가에서 이 서비스를 사용할 때, 발생할 수 있는 문제는 어떤 것들이 있을까?</p>
<p>한번, 데이터베이스에 한국시간 기준 <code>2021-01-02 00:00:00</code> 으로 새해 할인 이벤트 만료 시간이 저장되어있다고 생각해보자!
한국 시간 <code>2021-01-02 00:00:01</code> 에 한국에 있는 사용자는 이벤트를 이용할 수 없지만,
한국보다 약 13시간 느린 미국에 있는 사용자는 이미 13시간 전부터 이벤트가 만료되었을 것이다!
우리는 모든 나라의 1월 2일에 이벤트가 만료되길 바랐지만, 의도대로 동작하지 않았다...!</p>
<p>그렇다면, 한국시간을 기준으로 미국에서는 <code>+13시간</code>, 독일에서는 <code>+7시간</code> 등 나라마다 한국을 기준으로 계산해주어야 하는걸까..?
한국과 다른 나라의 시차를 일일이 계산해서 적용한다면, 실수하기 너무 쉬울 것 같다.
어떤 시간을 기준으로 해야 유연하고, 안전하게 시간 데이터를 관리할 수 있을까?</p>
</br>
</br>

<h1 id="데이터베이스-기준--utc">데이터베이스 기준 : UTC</h1>
<ul>
<li>&quot;국제적인 표준 시간&quot; 이다.</li>
<li>대부분의 시간 라이브러리는 <code>UTC</code>를 기준으로 만들어져있다! 따라서, 여러 다른 시간대로 변환하기 매우 편리하다.</li>
<li>우리는 <code>UTC</code>를 기준으로 데이터를 저장하기로 결정했다!</li>
</ul>
<h3 id="저장-형태">저장 형태</h3>
<ul>
<li><code>UCT</code> 기준 <code>yyyy-MM-dd hh:mm:ss</code> 문자열 형태로 저장하고자 한다!</li>
<li>사람이 읽기 쉽고, 필요한 정보를 적절히 담았다.</li>
</ul>
</br>
</br>


<h1 id="서버-기준--local">서버 기준 : Local</h1>
<ul>
<li>서버에서는 <code>JVM timezone</code> 을 로컬 타임존으로 설정한다.</li>
<li>데이터베이스에 있는 데이터를 가져와서 사용자에게 보여줄 때에는 로컬 타임을 적용하여 변환해서 보여준다.</li>
</ul>
<h3 id="표현-형태">표현 형태</h3>
<ul>
<li><code>ISO 8601 포맷 문자열</code>을 사용하고자 한다.</li>
<li><code>ISO</code> 는 데이터 교환을 다루는 국제 표준 방식이다. </li>
<li><code>2021-08-10T17:40:00+09:00</code> 처럼 시간 뒤에 <code>timezone offset</code> 이 표시되어 있기 때문에 커뮤니케이션 미스 등으로 인한 실수의 염려가 적다!</li>
<li>사람이 읽기 쉽다는 점은 당연하다!</li>
</ul>
</br>
</br>

<h3 id="시간-타입">시간 타입</h3>
<h4 id="timestamp-vs-zoneddatetime"><code>Timestamp</code> VS <code>ZonedDateTime</code></h4>
<ul>
<li>결론 : <code>ZonedDateTime</code> 을 쓰는 것이 더 좋다.</li>
<li><code>Timestamp</code> 는 기존 <code>java.util.Date</code> 를 상속받았기 때문에 그것의 문제점을 그대로 안고 있다!</li>
</ul>
<h4 id="javautildate-의-문제점"><code>java.util.Date</code> 의 문제점</h4>
<ul>
<li>불변 객체가 아니다.</li>
<li>상수 필드 남용<ul>
<li><code>calendar.add(Calendar.SECOND, 2);</code></li>
</ul>
</li>
<li>헷갈리는 월 지정<ul>
<li>1월을 <code>0</code>으로 표현하는 문제</li>
</ul>
</li>
<li>일관성 없는 요일 상수</li>
<li><code>Date</code>와 <code>Calendar</code> 객체의 역할 분담<ul>
<li>필요한 기능을 사용하기 위해서 Calendar 객체를 생성하고 Date 객체를 생성하는 프로세스를 거치기 때문에 번거롭고 생성 비용이 비싸다.</li>
</ul>
</li>
<li>기타 <code>java.util.Date</code> 하위 클래스의 문제</li>
</ul>
</br>
</br>

<h1 id="reference">Reference</h1>
<ul>
<li><a href="https://d2.naver.com/helloworld/645609">https://d2.naver.com/helloworld/645609</a></li>
<li><a href="https://velog.io/@lsb156/Instant-vs-LocalDateTime">https://velog.io/@lsb156/Instant-vs-LocalDateTime</a></li>
<li><a href="https://jojoldu.tistory.com/361">https://jojoldu.tistory.com/361</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[검색 성능 최적화하기]]></title>
            <link>https://velog.io/@now_iz/%EA%B2%80%EC%83%89-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@now_iz/%EA%B2%80%EC%83%89-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 29 Jul 2021 21:24:01 GMT</pubDate>
            <description><![CDATA[<h1 id="검색-기능">검색 기능</h1>
<p><img src="https://images.velog.io/images/now_iz/post/452bc78d-d7fe-4751-bd8d-7efb33fbde15/image.png" alt="">
쿠팡에는 <strong>키워드로 상품을 검색</strong>하는 기능이 있다
이 기능을 더 효율적인 쿼리로 구현하기 위해서는 어떻게 할 수 있을지 고민해보았다!</p>
</br>
</br>

<h1 id="1-like">1. LIKE</h1>
<h3 id="쿼리">쿼리</h3>
<pre><code>SELECT * 
FROM coupang.PRODUCT
WHERE name LIKE &#39;%키워드%&#39;;</code></pre><h3 id="성능-테스트">성능 테스트</h3>
<ul>
<li>Execution Time<ul>
<li>2.862 sec</li>
</ul>
</li>
<li>Explain
<img src="https://images.velog.io/images/now_iz/post/5dc47ea6-5b06-46e5-bb5f-abd5c2e832fc/image.png" alt=""><ul>
<li><code>type</code> : ALL<ul>
<li>인덱스를 타지 못하고, full scan 하는 것을 확인할 수 있다.</li>
</ul>
</li>
<li><code>rows</code> : 49488572<ul>
<li>전체 로우를 검사한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="문제점">문제점</h3>
<ul>
<li><code>LIKE</code> 에서 <code>물티슈%</code>와 같이, 와일드카드를 뒤쪽에 붙였을 때에는 인덱스를 잘 활용한다.</li>
<li>하지만, <code>%물티슈</code>, <code>%물티슈%</code> 와 같이<code>%</code> 와일드카드를 <strong>앞에 붙이면 인덱스를 활용하지 못하고, full scan</strong> 하여 성능이 매우 떨어지는 것이다.<ul>
<li>mysql 버전에 따라 <code>%물티슈</code> 처럼 앞에만 와일드카드를 붙이는 경우에는 인덱스를 탈 수 있다고 한다.</li>
</ul>
</li>
</ul>
</br>
</br>


<h1 id="2-fulltext">2. fulltext</h1>
<h3 id="쿼리-1">쿼리</h3>
<ol>
<li>FULLTEXT 인덱스 추가<pre><code>ALTER TABLE `coupang`.`PRODUCT` 
ADD FULLTEXT INDEX `name` (`name`) 
WITH PARSER NGRAM VISIBLE;</code></pre></li>
<li>검색 쿼리 작성
```</li>
</ol>
<p>-- QUERY 1
SELECT * 
FROM coupang.PRODUCT 
WHERE MATCH (<code>name</code>) AGAINST (&#39;2312341&#39; IN NATURAL LANGUAGE MODE);</p>
<pre><code>- 이 쿼리는 NGRAM 파서로 나누어진 단어들도 찾기 때문에 잘못된 결과가 나오기 쉽다.
- 위의 쿼리를 실행했을 때, 일치하지 않는 칼럼도 나오게 된다.
  ![](https://images.velog.io/images/now_iz/post/6a021756-cef4-48b7-a9af-474b1b71fadb/image.png)
</code></pre><p>-- QUERY 2
SELECT * 
FROM coupang.PRODUCT 
WHERE MATCH (<code>name</code>) AGAINST (&#39;2312341&#39; IN BOOLEAN MODE);</p>
<p>```</p>
<ul>
<li>첫번째 쿼리에서 모드를 <code>BOOLEAN</code> 으로 바꾸었다.</li>
<li>그러나 아래와 같은 캐시 제한에 걸렸고, 최대한으로 늘려보아도 부족하여 해결할 수 없었다.<blockquote>
<h4 id="error-fts-query-exceeds-result-cache-limit-mysql">[ERROR] FTS query exceeds result cache limit mysql</h4>
<ul>
<li>캐시 한도를 두 배로 올렸지만, 계속 같은 오류가 발생하였음
<code>SET GLOBAL innodb_ft_result_cache_limit = 4000000000;</code></li>
</ul>
</blockquote>
</li>
</ul>
<h3 id="성능-테스트-1">성능 테스트</h3>
<ul>
<li>Explain
<img src="https://images.velog.io/images/now_iz/post/7c26fdcd-3b26-486d-a422-4bf92160a6af/image.png" alt=""></li>
</ul>
</br>
</br>



<h1 id="3-검색-엔진">3. 검색 엔진</h1>
<ul>
<li>추후에 검색 엔진을 사용해서 검색 성능을 높여보겠다.</li>
</ul>
</br>
</br>


<h1 id="reference">reference</h1>
<ul>
<li><a href="https://stackoverflow.com/questions/10354248/optimizing-mysql-like-string-queries-in-innodb">https://stackoverflow.com/questions/10354248/optimizing-mysql-like-string-queries-in-innodb</a></li>
<li><a href="https://gongzza.github.io/database/mysql-fulltext-search/">https://gongzza.github.io/database/mysql-fulltext-search/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[페이징 기능을 성능 최적화하기]]></title>
            <link>https://velog.io/@now_iz/%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98-%EC%B5%9C%EC%A0%81%ED%99%94%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@now_iz/%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98-%EC%B5%9C%EC%A0%81%ED%99%94%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 17 Jul 2021 08:00:10 GMT</pubDate>
            <description><![CDATA[<h1 id="페이징-기능">페이징 기능</h1>
<p><img src="https://images.velog.io/images/now_iz/post/783fef6f-de6c-441f-a314-6e54985b293b/image.png" alt=""></p>
<p>우리가 따라하고 있는 쿠팡에서는 페이징 기능이 있다!
그래서 상품 목록을 조회하는 API 에서 페이지 번호와 상품 개수를 함께 돌려줘야 하기 때문에
페이징 기능을 구현해보기로 했다 😀
어떻게 하면, 대용량 데이터에서도 빠르게 페이지 정보와 상품 정보를 가져올 수 있을까?</p>
</br>
</br>


<h1 id="1-offset-기반-페이징">1. Offset 기반 페이징</h1>
<ul>
<li>가장 일반적인 페이징 방식으로서, <code>OFFSET</code> 과 <code>LIMIT</code> 을 이용한다.</li>
<li>그러나, 이 방법에는 <strong>큰 문제점</strong>이 있다. 앞에서 읽었던 행을 다시 읽어야 하기 때문에 페이징 쿼리가 뒤로갈수록 느려진다. <strong>뒤로 갈수록 버리지만 읽어야 할 행의 개수가 많아 점점 뒤로 갈수록 느려지는 것이다!</strong><img src="https://images.velog.io/images/now_iz/post/2e8e30d5-ca18-4818-8e3b-f92315f0bf87/image.png" width="70%">
### 쿼리
```
SELECT *
FROM coupang.PRODUCT
WHERE is_rocket = 1
LIMIT 120
OFFSET 30000000;
```
### 성능 테스트</li>
<li>전체 데이터<ul>
<li>약 5천만개</li>
</ul>
</li>
<li>execution time<ul>
<li>29 sec</li>
</ul>
</li>
<li>실행 계획
<img src="https://images.velog.io/images/now_iz/post/08f0bb01-911c-4e58-90dc-14dee5b675a8/image.png" alt=""></li>
</ul>
</br>
</br>

<h1 id="2-join-을-활용한-페이징">2. JOIN 을 활용한 페이징</h1>
<ul>
<li>기본적으로 mysql 은 인덱스가 가르키는 row 에 접근하여 가져오는 <code>Row lookup</code> 를 사용한다.</li>
<li>그런데, <code>Inner Select</code> 문에서 최대한 인덱스로 검색해서 id 를 가져온 다음에 <code>JOIN</code> 하여 <code>Row lookup</code> 을 최대한 늦추는 <code>Late row lookup</code> 을 활용하면, 성능을 향상 시킬 수 있다.</li>
</ul>
<h3 id="쿼리">쿼리</h3>
<pre><code>SELECT *
FROM coupang.PRODUCT as p
JOIN (
    SELECT id
    FROM coupang.PRODUCT
    LIMIT 120
    OFFSET 30000000
) AS t
ON p.id = t.id;</code></pre><h3 id="성능-테스트">성능 테스트</h3>
<ul>
<li>전체 데이터<ul>
<li>약 5천만개</li>
</ul>
</li>
<li>execution time<ul>
<li>9.408 sec</li>
<li><code>offest</code> 방식보다 약 <strong>70% 정도 성능이 향상되었다.</strong></li>
<li>그러나, 아직 너무 느리다!</li>
</ul>
</li>
<li>실행 계획
<img src="https://images.velog.io/images/now_iz/post/e930a153-3138-4b82-9f1b-1266e07c6c7f/image.png" alt=""></li>
</ul>
</br>
</br>

<h1 id="3-cursor-기반-페이징">3. Cursor 기반 페이징</h1>
<ul>
<li><code>Cursor</code> 기반 페이징 방식은 <strong>조회 시작 부분을 인덱스로 빠르게 찾아</strong> 매번 첫 페이지만 읽도록 하는 방식이다.</li>
<li>아무리 페이지가 뒤로 가더라도 <strong>처음 페이지를 읽은 것과 동일한 성능</strong>을 가지게 되므로, <code>Offset</code> 방식보다 <strong>훨씬 성능이 개선</strong>된다.</li>
<li>그러나, 이 방식은 원래 페이지 번호가 없고, 다음 페이지를 호출할 수 있는 키워드 등을 제공하는 페이징 기능이다. 아래와 같은 시스템 (흔히 SNS 게시물 조회 API에서 많이 볼 수 있다) 에서 사용한다.<img src="https://images.velog.io/images/now_iz/post/575d1f61-5d48-4ad1-9d12-042878c84caf/image.png" width="80%"></li>
<li>즉, <code>직전 조회 결과의 마지막 ID</code> 를 넘겨서 다음 페이지를 조회하는 방식인 것이다.<pre><code>SELECT *
FROM coupang.PRODUCT
WHERE id &lt; 직전_조회_결과의_마지막_id
LIMIT 페이지 사이즈</code></pre></li>
<li>쿠팡에서는 아래처럼 페이지 네비게이터를 사용하기 때문에, 반드시 바로 다음 페이지를 요청할 것이라는 보장이 없다. 1번 페이지를 조회했다가도, 5번 페이지를 조회할 수 있어야 했다!<img src="https://images.velog.io/images/now_iz/post/2f85a1c2-be7f-4de5-a6da-cd534027d325/image.png" width="50%"></li>
<li><code>cursor</code> 방식은 성능 면에서 압도적이기 때문에 최대한 활용해보려고 노력했고, <code>이전 페이지 목록 마지막 커서</code>, <code>현재 페이지 목록의 조회 커서 목록</code>, <code>다음 페이지 목록 시작 커서</code> 를 가져오는 방법 등을 생각했다. 하지만, 결국 모두 <strong>한계가 있어서 사용하기는 힘들었다.</strong></li>
</ul>
<h3 id="쿼리-1">쿼리</h3>
<pre><code>SELECT *
FROM coupang.PRODUCT
WHERE id &gt;= 조회_시작점_id AND is_rocket = 1
LIMIT 120;</code></pre><h3 id="성능-테스트-1">성능 테스트</h3>
<ul>
<li>전체 데이터<ul>
<li>약 5천만개</li>
</ul>
</li>
<li>execution time<ul>
<li>0.035 sec</li>
<li><code>offest</code> 방식보다 약 <strong>80배 이상 성능이 향상되었다.</strong></li>
<li>커서 목록을 가져오는 쿼리의 오버헤드가 있겠지만, 그것을 감안해도 압도적인 성능이다!</li>
</ul>
</li>
<li>실행 계획
<img src="https://images.velog.io/images/now_iz/post/1d927857-e5d7-4b95-ac1c-0901bec9be10/image.png" alt=""></li>
</ul>
</br>
</br>

<h1 id="4-테이블의-분리">4. 테이블의 분리</h1>
<p>처음으로 돌아와서, &quot;쿠팡에서는 어떻게 하고 있을까?&quot; 추측해보기로 했다. 쿠팡 사이트를 탐색한 결과, <strong>어떤 필터링 조건</strong>을 걸어도 <strong>항상 최대 17 페이지</strong>까지만 있는 것을 확인했다!
아마도 &quot;모든 필터링 조건에 따라서 <strong>테이블을 분리해서 관리</strong>하는 대신, <strong>offset 방식</strong>으로 가져오는 게 아닐까?&quot; 추측했다.
나중에 우리도 해당 방법으로 리팩토링을 시도해볼 생각이다!</p>
</br>
</br>


<h1 id="기타">기타</h1>
<blockquote>
<h4 id="대용량-더미-데이터를-생성하는-방법">대용량 더미 데이터를 생성하는 방법</h4>
<ol>
<li><a href="http://www.generatedata.com/">더미 데이터를 만들 때 이용한 사이트</a></li>
<li><a href="https://www.mssqltips.com/sqlservertip/5148/populate-large-tables-with-random-data-for-sql-server-performance-testing/">mysql 프로시저 이용하기</a></li>
</ol>
</blockquote>
<ul>
<li>프로시저 : 일련의 쿼리를 마치 하나의 함수처럼 실행하기 위한 쿼리의 집합 - <a href="https://stajun.tistory.com/entry/MySQL-%ED%94%84%EB%A1%9C%EC%8B%9C%EC%A0%80">참고</a></li>
</ul>
</br>
</br>

<h1 id="reference">reference</h1>
<ul>
<li><a href="https://jojoldu.tistory.com/528?category=637935">https://jojoldu.tistory.com/528?category=637935</a></li>
<li><a href="https://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/">https://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/</a></li>
<li><a href="https://mariadb.com/kb/en/pagination-optimization/">https://mariadb.com/kb/en/pagination-optimization/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Redis 설치 및 실행]]></title>
            <link>https://velog.io/@now_iz/Redis-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%8B%A4%ED%96%89</link>
            <guid>https://velog.io/@now_iz/Redis-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%8B%A4%ED%96%89</guid>
            <pubDate>Fri, 16 Jul 2021 08:40:26 GMT</pubDate>
            <description><![CDATA[<h1 id="설치실행">설치/실행</h1>
<p>mac은 brew를 이용하여 쉽게 설치할 수 있다.</p>
<ul>
<li><p>설치</p>
<pre><code>$ brew install redis</code></pre></li>
<li><p>시작/종료</p>
<pre><code>$ brew services start redis
$ brew services stop redis</code></pre></li>
<li><p>redis-cli 사용하기</p>
<pre><code>$ redis-cli</code></pre></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OOP] 객체 지향 설계 5원칙]]></title>
            <link>https://velog.io/@now_iz/OOP-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84-5%EC%9B%90%EC%B9%99</link>
            <guid>https://velog.io/@now_iz/OOP-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84-5%EC%9B%90%EC%B9%99</guid>
            <pubDate>Wed, 26 May 2021 17:26:42 GMT</pubDate>
            <description><![CDATA[<h1 id="객체-지향-설계-5원칙-solid">객체 지향 설계 5원칙 (SOLID)</h1>
<blockquote>
<p>응집도를 높이고, 결합도를 낮춰라 (High Cohesion, Loose Coupling)</p>
</blockquote>
</br>

<h2 id="srp-단일-책임-원칙">SRP (단일 책임 원칙)</h2>
<blockquote>
<p>어떤 클래스를 변경해야 하는 이유는 하나 뿐이다.</p>
</blockquote>
<ul>
<li>클래스/속성/메서드/패키지/모듈/프레임워크 등에도 적용된다</li>
<li>장점 : <strong>재사용성</strong>이 좋아진다<ul>
<li>A 기능과 B 기능으로 나누어져있으면, 다른 클래스에서 A 기능만 필요한 경우에는 A 만 가져다 쓸 수 있는 모듈이 된다.  </li>
</ul>
</li>
</ul>
</br>


<h2 id="ocp">OCP</h2>
<blockquote>
<p>확장은 열려있고, 변경은 닫혀있다.</p>
</blockquote>
<ul>
<li>장점 : <strong>유지보수성</strong>이 좋아진다<ul>
<li>JDBC에서 DB를 mysql 에서 oracle 로 바꾸어도 Connection 설정 부분만 바꾸면 된다.<ul>
<li>자바 애플리케이션은 JDBC 인터페이스로 인해 변화에 영향을 받지 않는다.<img src="https://images.velog.io/images/now_iz/post/7639c2d8-d391-4a69-8e31-d047d7fb4041/OCP.jpg" width="70%"></li>
</ul>
</li>
</ul>
</li>
<li>변하는 것과 변하지 않는 것을 구분해야 한다!</li>
</ul>
</br>


<h2 id="lsp-리스코프-치환-원칙">LSP (리스코프 치환 원칙)</h2>
<blockquote>
<p>서브 타입은 기반 타입으로 변경할 수 있어야 한다. (상속)</p>
</blockquote>
<ul>
<li>고래 -&gt; 포유류</li>
</ul>
</br>


<h2 id="isp-인터페이스-분리-원칙">ISP (인터페이스 분리 원칙)</h2>
<blockquote>
<p>자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안된다.</p>
</blockquote>
<ul>
<li>인터페이스는 최소한의 메서드만 제공해야 한다 </li>
</ul>
</br>


<h2 id="dip-의존-역전-원칙">DIP (의존 역전 원칙)</h2>
<blockquote>
<p>자신보다 변경되기 쉬운 것들에 의존하지 않는다.</p>
</blockquote>
<ul>
<li>변하기 어려운 <strong>상위 클래스/ 인터페이스/ 추상 클래스</strong>에 의존하라.
  <img src="https://images.velog.io/images/now_iz/post/2cc061b0-63b7-482a-9e24-d0b210822997/before.jpg" alt=""><img src="https://images.velog.io/images/now_iz/post/ec16d533-d596-4681-9987-68495f048ad6/after.jpg" alt=""><ul>
<li>자신보다 변하기 쉬운 것에 의존하던 관계 -&gt; <strong>인터페이스</strong>를 추가하여 의존성을 역전</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OOP] 객체 지향 4대 원칙]]></title>
            <link>https://velog.io/@now_iz/OOP-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5</link>
            <guid>https://velog.io/@now_iz/OOP-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5</guid>
            <pubDate>Wed, 26 May 2021 17:05:40 GMT</pubDate>
            <description><![CDATA[<h1 id="객체-지향의-필요성">객체 지향의 필요성</h1>
<blockquote>
<h4 id="q-객체-지향-프로그래밍-왜-해야할까">Q. 객체 지향 프로그래밍 왜 해야할까?</h4>
<p>A. 유지보수성 + 재사용성 👍</p>
</blockquote>
</br>
</br>




<h1 id="✨-객체-지향-4대-원칙">✨ 객체 지향 4대 원칙</h1>
<h3 id="1-캡슐화encapsulation">1. 캡슐화(Encapsulation)</h3>
<blockquote>
<p>정보 은닉</p>
</blockquote>
<ul>
<li><code>public</code> : 모두가 접근 가능</li>
<li><code>protected</code> : 상속 / 같은 패키지 내 클래스에서 접근 가능</li>
<li><code>default</code> : 같은 패키지 내 클래스에서 접근 가능</li>
<li><code>private</code> : 본인만 접근 가능</li>
</ul>
<h3 id="2-상속inheritance">2. 상속(Inheritance)</h3>
<blockquote>
<p>재사용 =&gt; <code>extends</code></p>
</blockquote>
<ul>
<li>하위 클래스 - 상위 클래스<ul>
<li>하위클래스 <code>is a kind of</code> 상위클래스</li>
<li>하위 클래스는 상위클래스 특성을 <strong>재사용</strong>하고, <strong>확장</strong>한다. </li>
<li>상위 클래스의 물려줄 특성이 많을수록 좋다 (LSP)<ul>
<li>상위 클래스가 너무 빈약하면, 불필요한 형변환이 자주 일어난다.</li>
</ul>
</li>
</ul>
</li>
<li><strong>인터페이스</strong><ul>
<li>다중 상속 대신 도입</li>
<li>어떤 객체가 <strong>해야할 일</strong>을 정의하는 <strong>추상</strong> 자료형 </li>
<li>구현 클래스 <code>is able to</code> 인터페이스 (ex. <code>Runnable</code>)</li>
<li>인터 페이스는 구현을 강제할 메서드가 적을수록 좋다 (ISP)</li>
</ul>
</li>
</ul>
<h3 id="3-추상화abstraction">3. 추상화(Abstraction)</h3>
<blockquote>
<p>모델링 =&gt; <code>class</code></p>
</blockquote>
<ul>
<li>클래스 VS 객체<ul>
<li><strong>클래스</strong> : 분류에 대한 <strong>개념</strong> -&gt; 같은 특성을 지닌 여러 객체를 총칭하는 집합의 개념 (ex.사람)</li>
<li><strong>객체</strong> : <strong>실체</strong> -&gt; 유일무이한 사물 (ex. 김연아)</li>
</ul>
</li>
<li>추상화
: 구체적인 것을 분해해서 <strong>관심 영역 (애플리케이션 경계)</strong> 에 있는 특성만 가지고 재조합하는 것 (= 모델링)</li>
</ul>
<h3 id="4-다형성-polymorphism">4. 다형성 Polymorphism</h3>
<blockquote>
<p>사용 편의 =&gt; 오버라이딩, 오버로딩</p>
</blockquote>
<ul>
<li>오버라이딩<ul>
<li>같은 메서드 이름 / 같은 인자 목록 / 상위 클래스의 메서드 <strong>재정의</strong></li>
<li>상위 클래스 타입의 객체 참조 변수에서 자동으로 하위 클래스가 오버라이딩한 메소드를 호출해 줌</li>
</ul>
</li>
<li>오버로딩<ul>
<li>같은 메서드 이름 / 다른 인자 목록 / 다수의 메서드 <strong>중복 정의</strong></li>
</ul>
</li>
</ul>
</br>
</br>



<blockquote>
<h4 id="번외">번외</h4>
<p>Q. 실무에서는 클래스와 인터페이스 중에 어떤 걸 사용하는 게 좋을까?
✏️ <em>인터페이스</em>
✏️ 클래스의 상속은 하위클래스가 100개라면, 상위클래스를 바꾸기 쉽지 않기 때문이다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OOP] 자바의 절차적/구조적 프로그래밍]]></title>
            <link>https://velog.io/@now_iz/OOP-%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%88%EC%B0%A8%EC%A0%81%EA%B5%AC%EC%A1%B0%EC%A0%81-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@now_iz/OOP-%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%88%EC%B0%A8%EC%A0%81%EA%B5%AC%EC%A1%B0%EC%A0%81-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Wed, 26 May 2021 16:53:20 GMT</pubDate>
            <description><![CDATA[<h1 id="자바의-절차적구조적-프로그래밍">자바의 절차적/구조적 프로그래밍</h1>
</br>

<h2 id="변수는-메모리에-저장된다">변수는 메모리에 저장된다</h2>
<h4 id="1-지역-변수">1) 지역 변수</h4>
<ul>
<li><strong>스택 프레임</strong>에 스택 프레임이 삭제될 때까지 존재</li>
<li>서로 다른 스택 프레임이면 지역 변수를 참조할 수 없다. <blockquote>
<p>메서드는 고유 공간이며, 다른 메서드의 지역 변수에 접근하려면 지역 변수의 메모리 주소 값인 포인터를 알아야 하는데 자바에서는 포인터를 사용할 수 없으므로 값만 전달해야 한다. (Call by value)</p>
</blockquote>
</li>
</ul>
<h4 id="2-클래스-멤버-변수">2) 클래스 멤버 변수</h4>
<ul>
<li><strong>메소드 영역</strong>에 JVM 종료될 때까지 존재</li>
<li>정적 메소드는 객체 존재 여부와 상관없이 사용할 수 있음<ul>
<li>유틸리티성 메서드 (ex. Math 클래스 내 메서드)</li>
</ul>
</li>
<li>전역 변수는 조심해서 써야 한다.<ul>
<li>가급적 &#39;읽기 전용&#39;으로 쓰자.</li>
<li>모든 객체가 동일한 값은 가지는 것을 전역 변수로 쓰면 좋다. (ex. 고양이 클래스의 다리 개수)</li>
</ul>
</li>
</ul>
<h4 id="3-객체-멤버-변수">3) 객체 멤버 변수</h4>
<ul>
<li><strong>힙</strong> 영역에 GC가 삭제할 때까지 존재 </li>
</ul>
<blockquote>
<h4 id="번외">번외</h4>
<p>초기화 없이 선언만 했을 때, 지역 변수에는 <strong>쓰레기값</strong>이 할당되고, 클래스 및 객체 멤버 변수는 <code>0</code>, <code>0.0</code>, <code>false</code> 등으로 <strong>초기화</strong>되는 이유는 무엇일까?
✏️ <em>클래스, 객체 멤버 변수는 공유 변수의 성격의 띄기 때문이다.</em></p>
</blockquote>
</br>
</br>

<h2 id="멀티-스레드-vs-멀티-프로세스">멀티 스레드 vs 멀티 프로세스</h2>
<ul>
<li><strong>메모리 안전성</strong> : 멀티 스레드 &lt; 멀티 프로세스 <ul>
<li>다른 프로세스의 메모리 영역을 절대 침범할 수 없다.</li>
</ul>
</li>
<li><strong>메모리 사용량</strong> : 멀티 스레드 &lt; 멀티 프로세스<ul>
<li>멀티 스레드는 메소드 영역(스태틱 영역), 힙 영역은 공유하므로 메모리를 적게 사용한다.</li>
</ul>
</li>
<li>요청 당 스레드 (Servlet), 요청 당 프로세스 (CGI)</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>