<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>seop-h.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sun, 10 Mar 2024 09:12:16 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>seop-h.log</title>
            <url>https://velog.velcdn.com/images/seop-h/profile/06c58420-9e9e-4202-9ef3-a8f71110ced1/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. seop-h.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/seop-h" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Gradle] 디펜던시 추가 시 소스코드(.java)까지 다운로드 (IntelliJ)]]></title>
            <link>https://velog.io/@seop-h/Gradle-%EB%94%94%ED%8E%9C%EB%8D%98%EC%8B%9C-%EC%B6%94%EA%B0%80-%EC%8B%9C-%EC%86%8C%EC%8A%A4%EC%BD%94%EB%93%9C.java%EA%B9%8C%EC%A7%80-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C-IntelliJ</link>
            <guid>https://velog.io/@seop-h/Gradle-%EB%94%94%ED%8E%9C%EB%8D%98%EC%8B%9C-%EC%B6%94%EA%B0%80-%EC%8B%9C-%EC%86%8C%EC%8A%A4%EC%BD%94%EB%93%9C.java%EA%B9%8C%EC%A7%80-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C-IntelliJ</guid>
            <pubDate>Sun, 10 Mar 2024 09:12:16 GMT</pubDate>
            <description><![CDATA[<p>스프링부트를 사용한 프로젝트를 하다보면 추가한 외부 라이브러리를 참고해야 할 일이 많다.
인텔리제이를 사용한다면, 반환값이나 파라미터, 간단한 내부 프로세스 정도가 궁금할 때는 해당 클래스나 메서드를 타고 들어가면 바로 볼 수가 있다. 더구나 이런 외부 라이브러리의 경우 각 클래스별, 메서드별로 javadoc을 사용하여 주석을 상세히 달아놓아서, 웬만한 경우엔 이것만 보고도 바로 그 라이브러리를 사용할 수 있을 정도이다.</p>
<h2 id="문제-발생">문제 발생</h2>
<p>이러한 주석 덕에 위 같은 방법을 이전부터 종종 사용했지만, 어느 순간부터 이런 주석이 보이지 않았다. 대신에 외부 라이브러리 파일을 열어보면 맨 위에 <code>Decompiled .class file, bytecode version: ...</code> 이라는 메시지가 뜨기 시작했다. </p>
<p><img src="https://velog.velcdn.com/images/seop-h/post/11fe85ad-4f91-40e9-a6cf-60877e5576e7/image.png" alt=""></p>
<p>찾아보니 이는 인텔리제이가 <code>Java Bytecode Decompiler</code> 라는 도구를 사용해, 이미 컴파일된 파일 (<code>.class</code>)을 자바 코드로 디컴파일해서 보여주고 있었던 것이다.</p>
<p>그래서 이전에 했던 프로젝트들을 찾아봤더니 모두 자바 소스코드(<code>.java</code>)를 직접 다운로드 받아서 그 파일을 보여주고 있었다.</p>
<p>물론 온전한 소스코드를 보고 싶을 때마다 위 사진 오른쪽 상단의 <code>Download Sources</code>를 눌러도 되겠지만, 필요할 때마다 일일히 그렇게 하기도 너무 번거로울 것 같아 한 번에 모든 외부 라이브러리의 소스 파일을 다운받을 수 있는 방법을 찾아보았다.</p>
<h2 id="해결">해결</h2>
<p>보통 Gradle 을 통해 스프링부트 프로젝트를 빌드해 사용했기 때문에, <code>build.grdle</code>에 어떤 설정 정보를 넣어주면 디펜던시를 추가할 때 클래스 파일 뿐만 아니라 소스 코드도 같이 다운로드할 수 있지 않을까 생각했다. 실제로 다음과 같은 설정 정보를 추가하면 된다는 글을 찾아볼 수 있었다.</p>
<pre><code class="language-java">plugins {
  ...
  id &#39;idea&#39;
}</code></pre>
<pre><code class="language-java">idea {
  module {
    downloadSources = true
  }
}</code></pre>
<p>이후로는 아래 사진처럼 모든 소스 파일이 전부 다운로드되었고, 내가 원하는 주석까지 잘 보여주었다.</p>
<p><img src="https://velog.velcdn.com/images/seop-h/post/88227042-0f44-4ede-8377-274fa653e1cd/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/seop-h/post/9e0acc46-1cbe-4c39-8e49-830ba7f91936/image.png" alt=""></p>
<p>여담으로 소스 파일이든 클래스 파일이든 로컬 상에서 다운받은 위치를 알고 싶으면 사진처럼 <code>Open in</code> &gt; <code>Path Popup</code> 를 클릭하면 된다.</p>
<p>예전에 스프링부트 프로젝트를 생성할 때는 별다른 설정 없이도 소스 파일을 다운받았었던 것 같은데, 왜 바뀐 것인지는 잘 모르겠다.
앞으로는 이 방법을 통해 소스 파일도 같이 다운받으면 될 것 같다.</p>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li><a href="https://blog.jetbrains.com/ko/2020/04/10/java-bytecode-decompiler-ko/">Java Bytecode Decompiler</a></li>
<li><a href="https://intellij-support.jetbrains.com/hc/en-us/community/posts/360006994800-Automatically-download-sources-of-dependencies-with-gradle">Automatically download sources of dependencies with gradle</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 다형성]]></title>
            <link>https://velog.io/@seop-h/Java-%EB%8B%A4%ED%98%95%EC%84%B1</link>
            <guid>https://velog.io/@seop-h/Java-%EB%8B%A4%ED%98%95%EC%84%B1</guid>
            <pubDate>Thu, 02 Nov 2023 13:57:36 GMT</pubDate>
            <description><![CDATA[<p>자바와 같은 객체지향언어를 공부하다 보면 <strong>다형성</strong>이라는 개념을 마주할 수 밖에 없다. 개인적으로는 이 부분이 객체지향언어를 배울 때 가장 어렵지 않을까 생각하는데, 그만큼 중요한 부분이기도 하기 때문에 지금부터 다형성에 관해 작성해 보겠다.</p>
<br>

<h2 id="1-다형성이란">1. 다형성이란</h2>
<p>다형성의 한자어를 그대로 풀이하면 <strong>&quot;다양한 형태를 가질 수 있는 성질&quot;</strong> 정도를 의미할 것이다. 그렇다면 누가 다양한 형태를 가질 수 있다는 걸까? 
자바로 어떤 프로그램을 만든다고 하면 모든 기능은 메서드를 통해 구현하고 그 메서드들은 클래스, 즉 객체 안에 있다. 그리고 프로그래머는 그러한 객체를 참조변수를 통해 이용한다. 이 <strong>참조변수</strong>가 다양한 형태를 가질 수 있는 것이다. 그러면 당연히 이 <strong>다양한 형태</strong>는 다양한 클래스 타입의 객체가 된다.</p>
<p>이를 조금 더 구체적으로 말하자면, 다형성은 <strong>조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스(객체)를 참조할 수 있는 성질</strong>이라고 할 수 있다.
자바에서는 다형성과 아주 밀접하게 연관된 개념들이 있는데, 바로 <strong>상속</strong>과 <strong>추상화</strong>이다.</p>
<br>
<br>

<h2 id="2-상속">2. 상속</h2>
<p>상속은 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것이다.</p>
<pre><code class="language-java">class Car {
    String color;
    int door;

    void drive() {
        //운전하는 기능
    }

    void stop() {
        //정지하는 기능
    }
}</code></pre>
<p>위와 같은 클래스가 있다고 하자. 그런데 자동차에 호스를 달아 소방차를 만들고 싶다면, 다시 처음부터 필요한 변수와 메서드를 하나하나 작성하는 것이 아니라 다음과 같이 기존 Car 클래스에 호스와 물을 뿌리는 기능만 추가해 만들면 된다. <strong>상속의 핵심은 코드를 재사용하는 것이다.</strong></p>
<pre><code class="language-java">class FireEngine extends Car {
    int fireHose;

    void water() {
        //물을 뿌리는 기능
    }
}</code></pre>
<p>이렇게 상속받은 클래스의 인스턴스를 만들어 사용하고자 하면 아마 다음과 같이 할 것이다.</p>
<pre><code class="language-java">FireEngine fireEngine = new FireEngine();</code></pre>
<p>물론 이렇게도 할 수 있지만, 다음 방식으로도 쓸 수 있다. <strong>이것이 다형성을 이용한 방식이다.</strong></p>
<pre><code class="language-java">Car car = new FireEngine();</code></pre>
<p>그러나 이렇게 조상타입의 참조변수(car)로 자손타입의 인스턴스(FireEngine 객체)를 참조할 때에는 주의해야할 부분이 있는데, 자손타입의 클래스가 가지고 있는 멤버(메서드나 변수)는 사용이 불가능하다는 것이다. 즉 <code>drive()</code>는 가능하지만 <code>water()</code>는 불가능하다. </p>
<pre><code class="language-java">Car car = new FireEngine();

car.drive() //O
car.water() //X</code></pre>
<p><strong>참조변수가 쓸 수 있는 멤버는, 참조하고 있는 인스턴스의 타입(FireEngine)이 아니라 그 참조변수의 타입(Car)을 따라간다.</strong></p>
<p>만약에 <code>car</code>에서 <code>water()</code>와 같은 FireEngine에만 있는 멤버를 쓰고 싶으면 FireEngine으로 강제 형변환을 해주면 된다.</p>
<pre><code class="language-java">(FireEngine) car.water()</code></pre>
<p>이런 식으로 조상타입의 참조변수를 자손타입으로 형변환하는 것을 <strong>다운캐스팅</strong>이라 하고, 반대로 자손타입의 참조변수를 조상타입으로 형변환하는 것을 <strong>업캐스팅</strong>이라 한다. 또한 다운캐스팅은 형변환을 명시해줘야하지만 업캐스팅은 이를 생략할 수 있다.
<br></p>
<p>한편 다음과 같이 코드를 작성하면 아예 컴파일 시점에 에러가 발생한다.</p>
<pre><code class="language-java">FireEngine fireEngine = new Car();</code></pre>
<p>위에서 참조변수가 쓸 수 있는 멤버는 그 참조변수의 타입을 따라간다고 했다. <code>fireEngine</code>도 이에 따라서 <code>fireEngine.water()</code>를 실행하고자 할 수도 있다. 그러나 <code>fireEngine</code>이 실제로 참조하고 있는 것은 Car 클래스의 인스턴스이므로 여기에는 <code>water()</code>라는 기능이 없다. 즉 자손타입의 참조변수로 조상타입의 인스턴스를 참조하는 것은, 존재하지 않는 멤버를 사용하고자 할 가능성이 있으므로 허용하지 않는 것이다.</p>
<p>그러면 업캐스팅이라는 것도 있으니 이걸 써서 위의 코드를 다음과 같이 고치면 되지 않을까?</p>
<pre><code class="language-java">Car car = new Car();
FireEngine fireEngine = (FireEngine) car;</code></pre>
<p>이것도 안된다. <strong>형변환은 참조변수의 타입을 변환하는 것이지, 인스턴스 자체를 변환하는 것은 아니다.</strong> <code>car</code>는 지금 Car의 인스턴스를 참조하고 있다. Car의 인스턴스는 <code>water()</code>나 <code>fireHose</code> 처럼 FireEngine에서 추가한 멤버를 가지고 있지 않다. 즉 위에서 얘기한 것과 똑같은 이유다. 존재하지 않는 멤버를 사용하고자 할 가능성이 있어서 안되는 것이다.</p>
<p>업캐스팅이 가능한 경우는 다음과 같다.</p>
<pre><code class="language-java">Car car = new FireEngine();
FireEngine fireEngine = (FireEngine) car;</code></pre>
<p>정리하자면 <strong>조상타입의 참조변수를 자손타입의 참소변수로 형변환하는 것(업캐스팅)이 가능하긴 하지만, 조상타입의 참조변수가 실제로 참조하는 인스턴스도 조상타입이면 형변환이 안된다.</strong></p>
<br>
<br>

<h2 id="3-추상화">3. 추상화</h2>
<p>...작성중...</p>
<h3 id="참고-자료">참고 자료</h3>
<ul>
<li>Java의 정석(남궁성 저)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Web Server와 WAS의 차이점]]></title>
            <link>https://velog.io/@seop-h/Web-Server%EC%99%80-WAS%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@seop-h/Web-Server%EC%99%80-WAS%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Tue, 10 Oct 2023 05:35:54 GMT</pubDate>
            <description><![CDATA[<h2 id="1-web-server">1. Web Server</h2>
<ul>
<li>HTTP 기반으로 동작한다.</li>
<li>웹 브라우저(클라이언트)로부터 파일 경로를 HTTP 요청으로 받는다.</li>
<li>경로상의 리소스가 정적HTML, 이미지, 영상 등 정적 리소스면 클라이언트에게 HTTP 응답으로 이를 제공한다.</li>
<li>만약 클라이언트가 동적 컨텐츠를 요청하면 WAS로 그 요청을 위임할 수 있다.</li>
<li>종류: Nginx, Apache Server, IIS 등</li>
</ul>
<p><img src="https://velog.velcdn.com/images/seop-h/post/8a320d19-7749-4a86-8103-ad0f35e8830b/image.png" alt="static pages"></p>
<br>


<h2 id="2-wasweb-application-server">2. WAS(Web Application Server)</h2>
<ul>
<li>HTTP 기반으로 동작한다.</li>
<li>대부분의 WAS는 Web Server를 내장하고 있다.</li>
<li>추가적으로 WAS는 동적 리소스를 처리한다.</li>
<li>웹 브라우저나 웹 서버로부터 HTTP 요청을 받는다.</li>
<li>DB 조회, 애플리케이션 로직 처리를 통해 동적 리소스를 클라이언트(웹 브라우저, Web Server)에게 HTTP 응답으로 제공한다.</li>
<li>흔히 Application Server(App Server)라고도 부른다.</li>
<li>종류: Apache Tomcat, Jetty, JBoss 등</li>
</ul>
<p><img src="https://velog.velcdn.com/images/seop-h/post/3b22493c-5826-49a8-ac02-f591af7524fc/image.png" alt="dynamic pages"></p>
<br>


<h2 id="3-web-server와-was의-차이점">3. Web Server와 WAS의 차이점</h2>
<h3 id="1-프로토콜">1) 프로토콜</h3>
<ul>
<li>Web Server와 WAS는 기본적으로 HTTP를 사용한다.</li>
<li>파일이나 이메일 전송이 필요한 서버는 FTP나 SMTP를 사용하기도 한다.</li>
<li>추가적으로 WAS는 다른 소프트웨어와의 통신을 위해 별도의 프로토콜(RMI, RPC 등)을 사용할 수도 있다.</li>
</ul>
<h3 id="2-콘텐츠-유형">2) 콘텐츠 유형</h3>
<ul>
<li>Web Server는 요청이 오면 정적 컨텐츠를 곧바로 클라이언트에게 제공하거나 동적 컨텐츠 제공을 위해 WAS로 요청을 전달한다.</li>
<li>WAS는 요청이 오면 정적 컨텐츠나 동적 컨텐츠를 클라이언트에게 제공한다 (주로 동적 컨텐츠).</li>
</ul>
<h3 id="3-멀티스레딩">3) 멀티스레딩</h3>
<ul>
<li>대부분의 Web Server는 멀티스레딩을 지원하지 않고, WAS는 지원한다.</li>
<li>즉 WAS는 여러 클라이언트의 요청을 병렬적으로 처리해 효율성이 높다.</li>
</ul>
<br>

<h2 id="4-웹-시스템-구성">4. 웹 시스템 구성</h2>
<p><img src="https://velog.velcdn.com/images/seop-h/post/1d0a2a9a-d2a2-4254-9722-52cb43bb6826/image.png" alt="Web Service Architecture"></p>
<ul>
<li>보통 웹 시스템 구조를 설계할 때 WAS 앞에 별도의 Web Server를 두는 경우가 많다.</li>
<li>이렇게 하는 가장 대표적인 이유는 서버의 부하를 분산시키기 위함이다.</li>
<li>정적 리소스만을 요청하는 경우 Web Server가 전부 처리하고, 동적 리소스를 요청하고 중요한 애플리케이션 로직을 처리할 때만 WAS가 사용되도록 한다.</li>
<li>이렇게 웹 시스템을 구성하면 WAS나 DB 서버에 장애가 발생했을 때, Web Server를 사용해 사용자에게 오류 화면을 제공하기에도 용이하다.</li>
<li>어느 종류의 리소스가 더 많이 사용되는지에 따라 개별적으로 서버를 증설할 수 있다. 정적 리소스가 많이 사용되면 Web Server를 증설하고, 애플리케이션 리소스가 많이 사용되면 WAS를 증설하는 식이다. 즉 효율적으로 리소스를 관리할 수 있다.</li>
</ul>
<br>


<h2 id="참고자료">참고자료</h2>
<ul>
<li><a href="https://gmlwjd9405.github.io/2018/10/27/webserver-vs-was.html">[Web] Web Server와 WAS의 차이와 웹 서비스 구조</a></li>
<li><a href="https://www.youtube.com/watch?v=mcnJcjbfjrs&amp;t=281s">[10분 테코톡] 알리의 Web Server vs WAS</a></li>
<li><a href="https://aws.amazon.com/compare/the-difference-between-web-server-and-application-server/?nc1=h_ls">What’s the Difference Between a Web Server and an Application Server?</a></li>
<li><a href="https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1">스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술(김영한)</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 level 2 - 교점에 별 만들기 (Java)]]></title>
            <link>https://velog.io/@seop-h/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-level-2-%EA%B5%90%EC%A0%90%EC%97%90-%EB%B3%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-Java</link>
            <guid>https://velog.io/@seop-h/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-level-2-%EA%B5%90%EC%A0%90%EC%97%90-%EB%B3%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-Java</guid>
            <pubDate>Thu, 14 Sep 2023 14:16:25 GMT</pubDate>
            <description><![CDATA[<ul>
<li><a href="https://school.programmers.co.kr/learn/courses/30/lessons/87377">문제 링크</a><h2 id="1-접근-방법">1. 접근 방법</h2>
문제를 읽어 보면, 교점도 구해야 하고 그 교점을 모아두는 컬렉션도 필요할 듯 하다. 그래서 다른 코딩테스트 문제들처럼 한 메서드에 모든 코드를 넣어두기 보다는, 새로운 클래스들을 생성해서 문제를 푸는게 좋을 듯 하다.</li>
</ul>
<p>한가지 유의할 점이 있는데, 데이터 타입을 잘 지정해야 한다. 직선의 계수가 int 타입이라고 교점 역시 int의 범위 내에 존재하진 않는다. 문제에 교점에 대한 범위 제한이 없으므로 long 타입으로 지정하는 것이 안전하다. 실제로 교점을 int로 여기고 문제를 풀면 오버플로우가 일어나 테스트 케이스 중 몇 개는 틀린다.</p>
<h2 id="2-전체-코드">2. 전체 코드</h2>
<pre><code class="language-java">import java.util.*;

class Solution {
    public String[] solution(int[][] line) {
        PointSet points = new PointSet();

        for(int i=0; i&lt;line.length; i++) {
            for(int j=i+1; j&lt;line.length; j++) {
                Interpoint point = Interpoint.makeInterpoint(line[i],line[j]);
                if (point != null) points.addPoint(point);
            }
        }

        return points.makeGrid();
    }
}

//두 직선의 교점 클래스
class Interpoint {
    //교점 (x,y)의 데이터 타입은 long으로 설정
    private final long x;
    private final long y;

    Interpoint(long x, long y) {
        this.x = x;
        this.y = y;
    }

    long getX(){
        return x;
    }

    long getY(){
        return y;
    }

    //두 직선 line1, line2의 교점 반환
    static Interpoint makeInterpoint(int[] line1, int[] line2) {
        //line1, line2가 평행하거나 교점이 정수가 아니면 null 반환
        long denominator = (long)line1[0]*line2[1]-(long)line1[1]*line2[0];
        if (denominator == 0) return null;

        long moleculeX = (long)line1[1]*line2[2]-(long)line1[2]*line2[1];
        long moleculeY = (long)line1[2]*line2[0]-(long)line1[0]*line2[2];

        if (moleculeX%denominator != 0 || moleculeY%denominator != 0) return null;

        //교점이 정수면 Interpoint 인스턴스 반환
        return new Interpoint(moleculeX/denominator, moleculeY/denominator);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Interpoint that = (Interpoint) o;
        return x == that.x &amp;&amp; y == that.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }
}

//교점들의 집합 클래스
class PointSet{
    private final Set&lt;Interpoint&gt; points;
    private long maxX, minX, maxY, minY;

    PointSet() {
        this.points = new HashSet&lt;&gt;();
    }

    //교점을 교점집합 안에 저장
    void addPoint(Interpoint point) {
        points.add(point);

        if (points.size()==1) {
            maxX=minX=point.getX();
            maxY=minY=point.getY();
        } else {
            maxX=Math.max(maxX,point.getX());
            minX=Math.min(minX,point.getX());
            maxY=Math.max(maxY,point.getY());
            minY=Math.min(minY,point.getY());
        }
    }

    //교점을 나타내는 격자판 생성
    String[] makeGrid() {
        String[] result = new String[(int)(maxY-minY+1)];
        int ind=0;

        for(long j=maxY; j&gt;=minY; j--) {
            StringBuffer buffer = new StringBuffer();
            for(long i=minX; i&lt;=maxX; i++) {
                if (points.contains(new Interpoint(i,j))) buffer.append(&#39;*&#39;);
                else buffer.append(&#39;.&#39;);
            }
            result[ind++] = new String(buffer);
        }

        return result;
    }
}</code></pre>
<h2 id="3-코드-분석">3. 코드 분석</h2>
<p>중요한 부분들을 하나씩 살펴보자.</p>
<p>기본적으로 여러 직선들의 교점을 전부 살피려면 두 직선을 골라서 확인하면 된다.</p>
<pre><code class="language-java">for(int i=0; i&lt;line.length; i++) {
            for(int j=i+1; j&lt;line.length; j++) {
                ......
            }
        }</code></pre>
<h3 id="1-interpoint-클래스">1) Interpoint 클래스</h3>
<p>Interpoint 클래스의 두 필드 x, y는 오버플로우가 일어나지 않게 long으로 타입을 지정해줬고, 두 직선의 교점을 구하는 메서드를 다음과 같이 작성했다.</p>
<pre><code class="language-java">static Interpoint makeInterpoint(int[] line1, int[] line2) {
        //line1, line2가 평행하거나 교점이 정수가 아니면 null 반환
        long denominator = (long)line1[0]*line2[1]-(long)line1[1]*line2[0];
        if (denominator == 0) return null;

        long moleculeX = (long)line1[1]*line2[2]-(long)line1[2]*line2[1];
        long moleculeY = (long)line1[2]*line2[0]-(long)line1[0]*line2[2];

        if (moleculeX%denominator != 0 || moleculeY%denominator != 0) return null;

        //교점이 정수면 Interpoint 인스턴스 반환
        return new Interpoint(moleculeX/denominator, moleculeY/denominator);
    }</code></pre>
<p>교점이 없거나 정수가 아니면 필요없는 것이므로 null을 반환하도록 했다.</p>
<p>또한 <code>equals()</code>와 <code>hashCode()</code>를 오버라이딩했는데, 이는 PointSet 클래스의 <code>makeGrid()</code>에서 쓰이는 <code>contains()</code>를 위해서다. <code>contains()</code>는 내부적으로 <code>equals()</code>와 <code>hashCode()</code>를 모두 사용해서 포함 여부를 확인하는데, 서로 다른 참조값을 가진 객체들은 이 두 메서드의 값이 다르기 때문이다. 따라서 서로 다른 객체라 하더라도 모든 필드값만 일치하면 <code>contains()</code>가 true를 반환하게끔 적절히 오버라이딩 해주었다.</p>
<pre><code class="language-java">    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Interpoint that = (Interpoint) o;
        return x == that.x &amp;&amp; y == that.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }</code></pre>
<h3 id="2-pointset-클래스">2) PointSet 클래스</h3>
<pre><code class="language-java">    private final Set&lt;Interpoint&gt; points;
    private long maxX, minX, maxY, minY;</code></pre>
<p>찾은 교점들은 PointSet 클래스의 points, 즉 교점 집합에 저장한다. 자료구조 타입이 Set인 이유는 두 쌍의 직선에서 교점을 찾았더니 그 교점이 같을 수도 있어서, 중복을 제거해주기 위함이다. maxX...minY는 교점이 존재하는 범위를 저장하기 위해 사용할 것이다.</p>
<pre><code class="language-java">    Interpoint point = Interpoint.makeInterpoint(line[i],line[j]);
    if (point != null) points.addPoint(point);</code></pre>
<p><code>Interpoint.makeInterpoint()</code>로 두 직선의 교점 객체를 만들고, 그 값이 null이 아니면 points에 저장한다. 이때 교점 집합의 최대, 최소 범위를 갱신한다. </p>
<pre><code class="language-java">    void addPoint(Interpoint point) {
        points.add(point);

        if (points.size()==1) {
            maxX=minX=point.getX();
            maxY=minY=point.getY();
        } else {
            maxX=Math.max(maxX,point.getX());
            minX=Math.min(minX,point.getX());
            maxY=Math.max(maxY,point.getY());
            minY=Math.min(minY,point.getY());
        }
    }</code></pre>
<p>마지막으로 <code>makeGrid()</code>로 모든 교점이 존재하는 가장 작은 크기의 격자판을 생성한다.</p>
<pre><code class="language-java">    String[] makeGrid() {
        String[] result = new String[(int)(maxY-minY+1)];
        int ind=0;

        for(long j=maxY; j&gt;=minY; j--) {
            StringBuffer buffer = new StringBuffer();
            for(long i=minX; i&lt;=maxX; i++) {
                if (points.contains(new Interpoint(i,j))) buffer.append(&#39;*&#39;);
                else buffer.append(&#39;.&#39;);
            }
            result[ind++] = new String(buffer);
        }

        return result;
    }</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 level 2 - 광물 캐기 (Java)]]></title>
            <link>https://velog.io/@seop-h/%EA%B4%91%EB%AC%BC-%EC%BA%90%EA%B8%B0-Java</link>
            <guid>https://velog.io/@seop-h/%EA%B4%91%EB%AC%BC-%EC%BA%90%EA%B8%B0-Java</guid>
            <pubDate>Tue, 25 Jul 2023 15:35:51 GMT</pubDate>
            <description><![CDATA[<ul>
<li><a href="https://school.programmers.co.kr/learn/courses/30/lessons/172927">문제 링크</a></li>
</ul>
<h2 id="1-접근-방법">1. 접근 방법</h2>
<p>최소 피로도를 구하라고 했으니 백트랙킹을 써도 되겠지만, 문제의 표를 자세히 보면 뭔가 다른 방법으로 풀어도 될 것 같은 생각이 든다.
<img src="https://velog.velcdn.com/images/seop-h/post/f93b6e1a-469e-4374-bb62-1b9ffacf755d/image.png" alt=""></p>
<p>곡괭이 이하의 등급을 가진 광물을 캐면 피로도가 1이고, 광물의 등급이 하나씩 높아질 때마다 피로도가 5배씩 증가한다. 돌 광물만 연속으로 5개를 캐는 게 아닌 이상 돌 곡괭이보단 철 혹은 다이아 곡괭이로 캐는 게 피로도가 무조건 적게 들고, 마찬가지로 철 혹은 돌 광물만 캐는 게 아닌 이상, 즉 다이아 광물이 하나라도 포함돼 있으면 다이아 곡괭이로 캐는 게 피로도가 가장 적다.</p>
<p>광물은 무조건 연속으로 5개씩을 캐야 하고, 그 5개 중 가장 높은 등급의 광물보다 하위 등급의 곡괭이로 캐는 것은 손해이기 때문에 이를 계산에 반영할 수 있도록 가중치를 부여한다.</p>
<h4 id="1-광물을-5개씩-묶어서-한-묶음에-대한-가중치를-매긴다">1. 광물을 5개씩 묶어서 한 묶음에 대한 가중치를 매긴다.</h4>
<pre><code class="language-java">int[][] status = new int[slength][4];

setStatus(status, minerals);</code></pre>
<p><code>slength</code>은 광물의 개수/5 를 올림한 숫자다. 단 이 숫자가 곡괭이 개수보다 적으면 <code>slength</code>을 곡괭이 개수로 변경한다. 2번에서 <code>status</code>를 정렬하기 때문에, 이렇게 하지 않으면 광물을 순서대로 캔다는 조건에 맞지 않는 경우가 정답으로 도출될 수도 있다.</p>
<p><code>setStatus()</code>에서 i번째 묶음 광물 5개의 상태를 <code>status[i][0]</code> ~ <code>status[i][2]</code>에 저장하고 <code>status[i][3]</code>에는 가중치를 저장한다.</p>
<p>가중치 = 25 x 다이아 광물 개수 + 5 x 철 광물 개수 + 돌 광물 개수</p>
<h4 id="2-그-가중치에-따라-status를-내림차순으로-정렬한다">2. 그 가중치에 따라 status를 내림차순으로 정렬한다.</h4>
<pre><code class="language-java">Arrays.sort(status, ((o1, o2) -&gt; o2[3] - o1[3]));</code></pre>
<p>즉 이 순서는 안 좋은 곡괭이를 쓸수록 피로도를 많이 잡아먹는 순서다. </p>
<h4 id="3-곡괭이를-좋은-거부터-하나씩-쓰면서-정렬된-status대로-광물을-캔다">3. 곡괭이를 좋은 거부터 하나씩 쓰면서 정렬된 status대로 광물을 캔다.</h4>
<hr>
<h2 id="2-코드">2. 코드</h2>
<pre><code class="language-java">  import java.util.*;

  class Solution {
        public int solution(int[] picks, String[] minerals) {
            int fatigue = 0;
            int slength = (int) Math.ceil(minerals.length / 5.0);
            if (slength &gt; picks[0] + picks[1] + picks[2]) slength = picks[0] + picks[1] + picks[2];
            int[][] status = new int[slength][4];

            setStatus(status, minerals);

            Arrays.sort(status, ((o1, o2) -&gt; o2[3] - o1[3]));
            int pInd = 0, sInd = 0;
            while (pInd &lt; picks.length) {
                if (picks[pInd] == 0) {
                    pInd++;
                    continue;
                }

                picks[pInd]--;
                if (pInd == 0) fatigue += status[sInd][0] + status[sInd][1] + status[sInd][2];
                else if (pInd == 1) fatigue += status[sInd][0] * 5 + status[sInd][1] + status[sInd][2];
                else fatigue += status[sInd][0] * 25 + status[sInd][1] * 5 + status[sInd][2];
                sInd++;
                if (sInd &gt;= status.length) break;
            }

            return fatigue;
        }

        private void setStatus(int[][] status, String[] minerals) {
            int cnt = 0, ind = 0;
            for (int i = 0; i &lt; minerals.length; i++) {
                cnt++;

                if (minerals[i].equals(&quot;diamond&quot;)) status[ind][0]++;
                else if (minerals[i].equals(&quot;iron&quot;)) status[ind][1]++;
                else status[ind][2]++;

                if (cnt == 5 || i == minerals.length - 1) {
                    cnt = 0;
                    status[ind][3] = 25 * status[ind][0] + 5 * status[ind][1] + status[ind][2];
                    ind++;
                }

                if (ind &gt;= status.length) break;
            }
        }
    }</code></pre>
]]></description>
        </item>
    </channel>
</rss>