<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>nice-sso.log</title>
        <link>https://velog.io/</link>
        <description>기억하려고 적는 개발 로그🌞</description>
        <lastBuildDate>Thu, 01 Dec 2022 08:36:07 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>nice-sso.log</title>
            <url>https://velog.velcdn.com/images/nice-sso/profile/8e8d18fc-23c3-48eb-837d-8b4696076a03/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. nice-sso.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/nice-sso" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Spring Data JPA] JPQL  ]]></title>
            <link>https://velog.io/@nice-sso/Spring-Data-JPA-JPQL</link>
            <guid>https://velog.io/@nice-sso/Spring-Data-JPA-JPQL</guid>
            <pubDate>Thu, 01 Dec 2022 08:36:07 GMT</pubDate>
            <description><![CDATA[<h3 id="overview">Overview</h3>
<p>JPA를 사용하여 서비스를 구현하다 보면 JPA의 Query Methods 만으로는 조회가 불가능한 경우가 존재한다.
이러한 경우 JPQL(Java Persistence Query Language)를 이용한 쿼리를 작성하여 조회를 할 수 있다.
JPQL을 작성하기 위한 방법으로 @Query annotation을 이용하여 JPQL을 작성하는 방법과, Native Query에 대해 적어보려고 한다.</p>
<h3 id="query">@Query</h3>
<p>@Query annotation은 더 구체적인 쿼리 메소드를 작성하기 위해 사용하며,기본적으로 JPA에서 사용하는 쿼리 문법인 JPQL을 사용한다.</p>
<h4 id="entity">Entity</h4>
<pre><code>@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Stock {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Long productId;
    private Long quantity;
}</code></pre><h4 id="repository">Repository</h4>
<pre><code>public interface StockRepository extends JpaRepository&lt;Stock, Long&gt; {

    @Query(&quot;select s from Stock s where s.id = :id&quot;)
    Stock findByIdWithPessimisticLock(Long id);
}</code></pre><p>위와 같이 Entity의 JpaRepository를 상속받는 인터페이스에 정의할 수 있다.
JPA의 Entity를 기반으로 하는 쿼리를 생성해주기 때문에 쿼리에 Table Name이 아닌 Entity이름이 들어간다. 또한 쿼리에 들어가는 내용은 엔티티 내에 있는 field의 이름을 가리킨다. 기본적으로 from 구문에 Entity의 객체를 선언하여 해당 객체의 속성명을 통해서 조건과 파라미터를 전달한다.</p>
<h4 id="query-annotation을-이용한-dto반환">Query annotation을 이용한 DTO반환</h4>
<p>여러개의 Entity의 속성이 정의되어 있는 DTO를 반환하기 위해서는 SELECT에서 해당 DTO의 생성자를 생성하는 코드를 적어주면 된다. </p>
<h4 id="dto">DTO</h4>
<pre><code>@Getter
@NoArgsConstructor
@AllArgsConstructor
public class StockDto {
    private Long id;
    private Long productId;
    private Long quantity;
}</code></pre><h4 id="repository-1">Repository</h4>
<pre><code>public interface StockRepository extends JpaRepository&lt;Stock, Long&gt; {

    @Query(value = &quot;select &quot; +
            &quot;new StockDto(s.id, s.productId, s.quantity) &quot; +
            &quot;from Stock s &quot; +
            &quot;left outer join Product p on s.productId = p.id &quot;)
    Stock findProductStock(Long id);
}</code></pre><h3 id="native-query">Native Query</h3>
<p>Native Query는 DB에서 사용하는 SQL문을 그대로 사용할 수 있게 해 준다.</p>
<pre><code>public interface LockRepository extends JpaRepository&lt;Stock, Long&gt; {

    @Query(value = &quot;select get_lock(:key, 3000)&quot;, nativeQuery = true)
    void getLock(String key);
}
</code></pre><p>위와 같이, nativeQuery옵션을 true로 사용하면 entity가 아닌 table을 사용하게 되며 field의 이름도 table의 column명을 써야 한다.</p>
<p><strong>장점:</strong> JPA의 쿼리 메서드는 여러 데이터를 업데이트 할때 update query를 여러번 실행시키지만 native query는 한 번의 query로 update처리가 가능하다.</p>
<p><strong>단점</strong>: Native Query는 특정 DB에 의존성을 가진 쿼리를 만들게 된다.
Database 종류로부터 자유롭다는 JPA의 장점에 어긋나므로 JPA를 사용하면서 Native Query 또한 사용하는 것은 지양해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[programmers] 주차 요금 계산]]></title>
            <link>https://velog.io/@nice-sso/programmers-%EC%A3%BC%EC%B0%A8-%EC%9A%94%EA%B8%88-%EA%B3%84%EC%82%B0</link>
            <guid>https://velog.io/@nice-sso/programmers-%EC%A3%BC%EC%B0%A8-%EC%9A%94%EA%B8%88-%EA%B3%84%EC%82%B0</guid>
            <pubDate>Wed, 23 Nov 2022 10:20:56 GMT</pubDate>
            <description><![CDATA[<h3 id="주차-요금-계산2022-kakao-blind-recruitment-문제">주차 요금 계산(2022 KAKAO BLIND RECRUITMENT) 문제</h3>
<h4 id="문제-설명">문제 설명</h4>
<p>주차장의 요금표와 차량이 들어오고 나간 기록이 주어졌을 때, 차량별로 주차 요금을 계산하는 문제이다. 누적 주차 시간이 기본시간 이하라면 기본 요금을 청구하고, 기본 시간을 초과했다면 기본요금에 더해서 초과한 시간의 단위 시간 마다 단위 요금을 청구한다.</p>
<h4 id="문제-풀이-1">문제 풀이 1.</h4>
<p>2중 for문을 사용하였고, records에 배열 길이만큼 돌면서 해당 인덱스의 출차 여부를 판단하여 입차 한 차량이라면 다음 for문을 돌면서 출차의 기록을 찾아서 시간 계산을 한다. 만약, 출차가 되지 않은 차량이라면 자정(23:59)에서 입차 시간을 빼서 계산을 해 주었다.</p>
<pre><code>public int[] solution(int[] fees, String[] records) {

    // 주차장의 요금표와 차량이 들어오고 나간 기록이 주어졌을 때 주차요금 계산
    Map&lt;String, Integer&gt; map = new TreeMap&lt;&gt;();
    int midnight = 23 * 60 + 59;
    for (int i = 0; i &lt; records.length; i++) {
        String[] arr = records[i].split(&quot; &quot;);
        if (arr[2].equals(&quot;OUT&quot;)) {
            continue;
        }

        int time = Integer.parseInt(arr[0].split(&quot;:&quot;)[0]) * 60
                + Integer.parseInt(arr[0].split(&quot;:&quot;)[1]);

        boolean out = false;
        for (int j = i + 1; j &lt; records.length; j++) {
            String[] arr2 = records[j].split(&quot; &quot;);
            if (arr[1].equals(arr2[1])) {
                int time2 = Integer.parseInt(arr2[0].split(&quot;:&quot;)[0]) * 60
                        + Integer.parseInt(arr2[0].split(&quot;:&quot;)[1]);

                map.put(arr[1], map.getOrDefault(arr[1], 0) + (time2 - time));
                out = true;
                break;
            }
        }
        // 출차된게 아니라면
        if (!out) {
            map.put(arr[1], map.getOrDefault(arr[1], 0) + (midnight - time));
        }
    }

    int[] answer = new int[map.size()];
    int index = 0;
    for (String key: map.keySet()) {
        double time = map.get(key);
        if (time &gt; fees[0]) {
            answer[index] += Math.ceil((time - fees[0]) / fees[2]) * fees[3];
        }
        answer[index] += fees[1];
        index++;
    }

    return answer;
}</code></pre><h4 id="풀이-2-결과">풀이 2 결과</h4>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/da98adaa-bdad-49a4-9614-c1eff32ebfd7/image.png" alt="">어려운 문제는 아니였는데 쪼개서 시간 계산하고, 요금 배열 부분의 인덱스 값을 잘 못 불러와서 오타 찾고 하는 부분에서 시간이 오래 걸렸다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[programmers] 두 큐 합 같게 만들기]]></title>
            <link>https://velog.io/@nice-sso/programmers-%EB%91%90-%ED%81%90-%ED%95%A9-%EA%B0%99%EA%B2%8C-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@nice-sso/programmers-%EB%91%90-%ED%81%90-%ED%95%A9-%EA%B0%99%EA%B2%8C-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Wed, 23 Nov 2022 06:47:24 GMT</pubDate>
            <description><![CDATA[<h3 id="두-큐-합-같게-만들기2022-kakao-tech-internship-문제">두 큐 합 같게 만들기(2022 KAKAO TECH INTERNSHIP) 문제</h3>
<h4 id="풀이-1">풀이 1.</h4>
<p>queue1과 queue2의 각각의 합계를 계산하여 더 큰쪽의 값을 pop해서 더 작은쪽의 리스트에 넣어주는 방식으로 구상을 해 보았다. while문을 돌리면서 계속 계산을 해 주는데, 한번 돌 때마다 index를 1씩 증가시켜주고 index가 queue배열 길이의 2배를 돌게 되면 모두 다 순환한다고 생각하여 종료문을 작성해 주었다.</p>
<pre><code>public int solution(int[] queue1, int[] queue2) {
    int answer = Integer.MAX_VALUE;

    // queue1의 0번째 인덱스부터 차례대로 뽑아서 queue2에 집어넣고 값 비교
    // 만약 queue1의 모든 값이 더 크다면 queue1에서 값을 pop하여 queue2에 넣는다.
    // 반대도 똑같이 진행 하면 최소 개수를 반환한다.

    // 길이의 가변성을 위하여 배열을 콜렉션으로 바꿔준다.
    List&lt;Integer&gt; list1 = Arrays.stream(queue1).boxed().collect(Collectors.toList());
    List&lt;Integer&gt; list2 = Arrays.stream(queue2).boxed().collect(Collectors.toList());

    // 먼저 두개의 합을 구하여서 더 큰 값의 queue의 원소를 pop한다.
    int sum1 = Arrays.stream(queue1).sum();
    int sum2 = Arrays.stream(queue2).sum();
    int count = 0;
    int index = 0;
    while (index &lt; queue1.length * 2) {
        index++;
        if (sum1 &gt; sum2) {
            int pop = list1.remove(0);
            list2.add(pop);
        } else {
            int pop = list2.remove(0);
            list1.add(pop);
        }
        count++;

        sum1 = list1.stream().mapToInt(Integer::intValue).sum();
        sum2 = list2.stream().mapToInt(Integer::intValue).sum();
        if (sum1 == sum2) {
            answer = Math.min(answer, count);
            count = 0;
        }
    }

    return answer == Integer.MAX_VALUE ? -1 : answer;
}</code></pre><h4 id="풀이-1-실행결과">풀이 1 실행결과</h4>
<p>하지만 100점중에 20점 밖에 점수가 안나왔고, 어떤 부분이 문제인지 다시 생각을 해 보았다. sum1과 sum2를 stream을 다시 구하지 않고 pop을 한 값을 sum에 더해주거나 빼주는 것으로 변경해도 될 것 같다. 또한, 어차피 최소 값이 충족 되었을 때 while문을 빠져 나오게 되므로 그 이후의 count값은 안 구해도 되므로 count = 0으로 초기화 시켜주고 다시 반복해서 값을 구하는 과정이 필요 없을 것 같다. 또한, 문제를 자세히 읽어보니 sum을 구할 때 산술 오버플로우가 생길수도 있다고 했으므로 long타입으로 변환을 해 줘야 할 것이다.
<img src="https://velog.velcdn.com/images/nice-sso/post/7df42190-038e-4a7e-9ed6-9e042bd73bf7/image.png" alt=""></p>
<h4 id="풀이-2">풀이 2.</h4>
<p>위의 생각들을 코드로 다시 옮겨 적어 보았다. list로 queue를 구현하던 것을 Queue자료구조로 다시 선언을 해 주고, sum은 long 타입으로 변환해 주었다. sum1과 sum2를 더해 중간값을 찾아주고 조건식에 넣어주는 방식으로 구현을 해 보았다. </p>
<pre><code>public int solution(int[] queue1, int[] queue2) {
    Queue&lt;Integer&gt; q1 = new ArrayDeque&lt;&gt;();
    Queue&lt;Integer&gt; q2 = new ArrayDeque&lt;&gt;();

    for (int num: queue1) q1.add(num);
    for (int num: queue2) q2.add(num);

    long sum1 = Arrays.stream(queue1).sum();
    long sum2 = Arrays.stream(queue2).sum();
    long mid = sum1 + sum2;
    if (mid % 2 == 1) {
        return -1;
    }
    mid /= 2;
    int p1 = 0, p2 = 0, len = queue1.length * 2;
    while (p1 &lt;= len &amp;&amp; p2 &lt;= len) {
        if (sum1 == mid) return p1 + p2;

        if (sum1 &gt; mid) {
            int num = q1.poll();
            sum1 -= num;
            sum2 += num;
            q2.add(num);
            p1++;
        } else {
            int num = q2.poll();
            sum1 += num;
            sum2 -= num;
            q1.add(num);
            p2++;
        }
    }
    return -1;
}</code></pre><h4 id="풀이-2-실행결과">풀이 2 실행결과</h4>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/db86035e-cc60-4953-a9e4-262e2a7c1679/image.png" alt=""></p>
<h4 id="느낀점">느낀점</h4>
<p>구현과정을 세세하게 적어보고 생각한다음 코드로 옮기면 생각하는 시간이 길더라도 오히려 정작 코드를 치고 수정해 나가는 방식보다 짧을 것이라는 생각이 든다. 어차피 코딩테스트도 하나의 시험이라 정해진 시간이 있기 때문에, 구현에 앞서 모든 경우의 수를 잘 생각해보고 코드로 옮기는 습관을 잘 들여서 시간을 절약해야 겠다고 생각했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[programmers] 할인 행사]]></title>
            <link>https://velog.io/@nice-sso/programmers-%ED%95%A0%EC%9D%B8-%ED%96%89%EC%82%AC</link>
            <guid>https://velog.io/@nice-sso/programmers-%ED%95%A0%EC%9D%B8-%ED%96%89%EC%82%AC</guid>
            <pubDate>Tue, 22 Nov 2022 14:55:58 GMT</pubDate>
            <description><![CDATA[<h3 id="할인-행사-문제">할인 행사 문제</h3>
<h4 id="문제-설명">문제 설명</h4>
<p>마트에서 회원을 대상으로 매일 한 가지 제품을 할인하는 행사를 한다. 정현이는 자신이 원하는 제품과 수량이 할인하는 날짜와 10일 연속으로 일치할 경우에 맞춰서 회원가입을 하려 한다.</p>
<h4 id="문제-풀이-1">문제 풀이 1.</h4>
<p>먼저, 원하는 상품과 수량을 담을 map과 discount 배열을 돌면서 넣어줄 map을 선언해 주었다. 그다음 while문을 돌면서 10개씩 제품을 map에 담아 비교해주고 동일하다면 answer를 1 증가시켜 주었다.</p>
<pre><code>public int solution(String[] want, int[] number, String[] discount) {
    int answer = 0;

    Map&lt;String, Integer&gt; map = new HashMap&lt;&gt;();
    Map&lt;String, Integer&gt; basket = new HashMap&lt;&gt;();
    for (int i = 0; i &lt; want.length; i++) {
        map.put(want[i], number[i]);
        basket.put(want[i], 0);
    }

    int index = 0;
    int nowIndex = 0;
    int len = 0;
    while (true) {
        // 원하는 제품이 모두 들어있다면 answer 1증가시킴
        if (basket.equals(map)) {
            answer++;
        }

        if (nowIndex == discount.length) {
            break;
        }

        // 10일 동안의 제품을 모두 확인했으면 이전 index의 제품은 지워준다.
        if (len == 10) {
            basket.put(discount[index], basket.get(discount[index]) - 1);
            index++;
            len -= 1;
            continue;
        }

        // 현재 인덱스에서 원하는 제품이 아니면 다음 인덱스로 넘어감
        if (!map.containsKey(discount[index])) {
            index++;
            nowIndex++;
            continue;
        }

        // 현재 인덱스부터 10일동안의 할인 제품 목록을 확인한다.
        if (map.containsKey(discount[nowIndex])) {
            basket.put(discount[nowIndex], basket.get(discount[nowIndex]) + 1);
            nowIndex++;
            len++;
        }
    }

    return answer;
}</code></pre><h4 id="풀이-1-실행결과">풀이 1 실행결과</h4>
<p>코드 실행은 통과 하였는데, 정확성 채점하기에서 시간초과가 났다. 구현이 잘못되었다 판단되어 풀이 2번처럼 for문으로 코드를 다시 작성하였다.
<img src="https://velog.velcdn.com/images/nice-sso/post/13732b68-e1ea-4767-aa73-9fdbb9fe7673/image.png" alt=""></p>
<h4 id="문제풀이-2">문제풀이 2.</h4>
<p>풀이 1과 map에 원하는 상품과 수량을 넣는 과정은 동일하고 할인하는 요일에 포함이 되어있는지 확인하는 과정만 수정해 보았다.
10일씩 세일 상품들을 확인하면서 map에 넣어주고 원하는 제품과 수량을 담아뒀던 map과 비교하여 동일하다면 answer를 1증가 시켜 주었다.</p>
<pre><code>public int solution(String[] want, int[] number, String[] discount) {
    int answer = 0;

    Map&lt;String, Integer&gt; map = new HashMap&lt;&gt;();
    for (int i = 0; i &lt; want.length; i++) {
        map.put(want[i], number[i]);
    }

    for (int i = 0; i &lt;= discount.length - 10; i++) {
        Map&lt;String, Integer&gt; basket = new HashMap&lt;&gt;();
        for (int j = 0; j &lt; 10; j++) {
            String item = discount[i + j];
            basket.put(item, basket.getOrDefault(item, 0) + 1);
        }
        if (basket.equals(map)) {
            answer++;
        }
    }

    return answer;
}</code></pre><h4 id="풀이-2-실행결과">풀이 2 실행결과</h4>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/12ba641b-2e97-4254-9aaa-c3b473c5b6fe/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[programmers] 연속 부분 수열 합의 개수]]></title>
            <link>https://velog.io/@nice-sso/programmers-%EC%97%B0%EC%86%8D-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4-%ED%95%A9%EC%9D%98-%EA%B0%9C%EC%88%98</link>
            <guid>https://velog.io/@nice-sso/programmers-%EC%97%B0%EC%86%8D-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4-%ED%95%A9%EC%9D%98-%EA%B0%9C%EC%88%98</guid>
            <pubDate>Mon, 21 Nov 2022 05:36:05 GMT</pubDate>
            <description><![CDATA[<h3 id="연속-부분-수열-합의-개수-문제">연속 부분 수열 합의 개수 문제</h3>
<h4 id="문제-설명">문제 설명</h4>
<p>원형 수열(일반적인 수열에서 처음과 끝이 연결된 형태의 수열)의 연속하는 부분 수열의 합으로 만들 수 있는 수가 모두 몇 가지인지 확인하는 문제
<img src="https://velog.velcdn.com/images/nice-sso/post/3ffc6e25-1e60-4437-86df-95ec2e1b5cc5/image.png" alt="원형수열"></p>
<h4 id="문제-풀이">문제 풀이</h4>
<p>처음에 for문으로 코드를 작성하였는데 인덱스를 계산하기가 어려워서 while문으로 바꾸는 과정에서 시간을 많이 잡아먹었다. 경우의 수를 잘 생각하고 코드를 짠다면 짧은 시간 안에 풀 수 있는 문제였던 것 같다.</p>
<p>이 문제에서 가장 중요하게 생각해야 할 부분은, index가 배열의 길이를 벗어난 경우이다. 배열이 7, 9, 1, 1, 4가 있고 3번째 인덱스부터 sum을 계산하고자 하는 경우 더해야할 인덱스들은 4, 7, 9, 1로 각각 4, 0, 1, 2 번째를 봐야 할 것이다. 풀이는 아래와 같다.</p>
<pre><code>public int solution(int[] elements) {
    Set&lt;Integer&gt; set = new HashSet&lt;&gt;();

    int len = 0;
    int nowIndex = 0;
    int index = 0;
    int sum = 0;
    while (true) {
        // 인덱스 0부터 1씩 증가해 가면서 수를 더한다.
        // len 만큼 다 돌았다면 인덱스를 1증가하여 다음 인덱스로 넘어간다.
        // 마지막 인덱스이고, len 만큼 다 돌았다면 종료한다.
        if (len == elements.length &amp;&amp; index == elements.length - 1) {
            break;
        }

        if (len == elements.length) {
            sum = 0;
            len = 0;
            index += 1;
            nowIndex = index;
        }

        // 인덱스가 범위를 벗어났다면 0으로 세팅해준다.
        if (nowIndex &gt; elements.length - 1) {
            nowIndex = 0;
        }

        sum += elements[nowIndex++];
        set.add(sum);
        len += 1;
    }

    return set.size();
}</code></pre><h4 id="실행-결과">실행 결과</h4>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/06de8db3-14d7-43c5-9305-6f388b8d21a5/image.png" alt="실행결과"></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[programmers] 택배상자]]></title>
            <link>https://velog.io/@nice-sso/programmers-%ED%83%9D%EB%B0%B0%EC%83%81%EC%9E%90</link>
            <guid>https://velog.io/@nice-sso/programmers-%ED%83%9D%EB%B0%B0%EC%83%81%EC%9E%90</guid>
            <pubDate>Sun, 20 Nov 2022 11:56:20 GMT</pubDate>
            <description><![CDATA[<h3 id="택배상자-문제">택배상자 문제</h3>
<h4 id="문제-설명">문제 설명</h4>
<p>영재가 실어야 하는 택배상자는 1번 부터 n번 상자까지 번호가 증가하는 순서대로 컨테이너 벨트에 일렬로 놓여 영재에게 전달된다. 상자는     1번 부터 순서대로 내릴 수 있으며, 순서가 아닌 택배는 보조 컨테이너 벨트에 실어서 보관할 수 있다. 보조 컨테이너 한쪽 면만 뚫려 있으며, 벨트 안에 있는 상자는 가장 나중에 보관한 상자부터 꺼낼 수 있다. 기사님이 알려준 순서가 적혀있는 orders에 적힌 순서대로 실어야 하며, 보조 컨테이너 벨트를 이용해도 기사님이 원하는 순서대로 상자를 싣지 못한다면 더이상 상자를 싣지 않는다.</p>
<h4 id="문제-풀이-1">문제 풀이 1.</h4>
<p>먼저 보조 컨테이너 벨트를 만들기 위해서 가장 나중에 들어간게 먼저 나오는 LIFO를 지원하는 stack자료구조를 이용하여, 순서가 아닌 상자들은 stack에 보관하도록 하였다. 해당 값이 stack의 맨 위에 있다면 해당 값을 제거하고 answer를 1증가 시켜주고, 없다면 1부터 해당 값의 -1까지의 값을 넣어주도록 하였다.</p>
<pre><code>public int solution(int[] orders) {
    int answer = 0;
    Stack&lt;Integer&gt; stack = new Stack&lt;&gt;();

    for (int i = 0; i &lt; orders.length; i++) {
        if (i == 0 &amp;&amp; orders[i] == 1) {
            answer++;
            continue;
        }

        if (stack.contains(orders[i])) {
            if (stack.peek() == orders[i]) {
                answer++;
                stack.pop();
            } else {
                break;
            }
        } else {
            answer++;
            for (int j = 1; j &lt;= orders[i] - 1; j++) {
                stack.push(j);
            }
        }

    }

    return answer;
}</code></pre><h4 id="풀이-1-실행-결과">풀이 1 실행 결과</h4>
<p>정확성이 50%가 나왔는데, for문을 2중으로 사용해서 그런건가 싶어서 아래와 같이 풀이 방식을 바꿔봤다.
<img src="https://velog.velcdn.com/images/nice-sso/post/2b18d825-01c0-42d7-bf7b-bec426f52f4d/image.png" alt=""></p>
<h4 id="문제-풀이-2">문제 풀이 2.</h4>
<p>2중 for문을 제거하고 변수로 index를 할당하고, 조건이 맞을 때에만 증가를 시켜줄 수 있도록 코드를 바꿔보았다. 
✅ orders[index]가 num(초기 값 1)과 같다면 answer를 1증가시켜주고, num도 1증가시켜줘서 다음 상자를 가져온다. 또한, index도 1증가시켜서, 택배 기사님이 원하는 다음 순서를 비교할 수 있도록 한다.
✅ 만약 stack의 맨 위의 값이 택배 기사님이 원하는 상자와 같다면 answer와 index를 증가시켜 주는데 이때에는 스택에 있는 값을 비교하는 것이기 때문에 num은 증가시키지 않고, 일치하는 stack의 값을 pop()해준다.
✅ 조건문에서 탈출하기 위해서는 아래와 같이 2가지를 체크해 준다.</p>
<ol>
<li>index가 배열의 범위를 벗어난 경우 종료</li>
<li>상자의 숫자가 배열의 개수를 벗어난 경우 종료</li>
</ol>
<pre><code>public int solution(int[] orders) {
    int answer = 0;
    Stack&lt;Integer&gt; stack = new Stack&lt;&gt;();

    int num = 1;
    int index = 0;
    while (true) {
        if (index &gt;= orders.length) {
            break;
        }

        if (orders[index] == num) {
            answer++;
            num++;
            index++;
            continue;
        } else if (!stack.isEmpty() &amp;&amp; stack.peek() == orders[index]) {
            answer++;
            index++;
            stack.pop();
            continue;
        }

        if (num &gt; orders.length) {
            break;
        }

        stack.push(num++);
    }

    return answer;
}</code></pre><h4 id="풀이-2-실행결과">풀이 2 실행결과</h4>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/23d9b098-c3ca-49a8-84c1-37d77c511c66/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[programmers] 롤케이크 자르기]]></title>
            <link>https://velog.io/@nice-sso/programmers-%EB%A1%A4%EC%BC%80%EC%9D%B4%ED%81%AC-%EC%9E%90%EB%A5%B4%EA%B8%B0-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@nice-sso/programmers-%EB%A1%A4%EC%BC%80%EC%9D%B4%ED%81%AC-%EC%9E%90%EB%A5%B4%EA%B8%B0-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4</guid>
            <pubDate>Sun, 20 Nov 2022 06:12:04 GMT</pubDate>
            <description><![CDATA[<h3 id="롤케이크-자르기-문제">롤케이크 자르기 문제</h3>
<h4 id="문제-설명">문제 설명</h4>
<p>롤케이크를 2조각으로 자르는데, 각 조각 위에 올라간 토핑의 개수가 같도록 자르는 문제이다. 토핑을 1, 2, 3, 4와 같이 번호로 표시했을 때, 케이크 위의 토핑들이 [1, 2, 1, 3, 1, 4, 1, 2] 순서로 올라가 있게 된다면 [1, 2, 1, 3], [1, 4, 1, 2]의 방법과 [1, 2, 1, 3, 1], [4, 1, 2] 총 2개의 방법으로 토핑이 동일하게 올라가도록 케이크를 자를 수 있다. 공평하게 자르지 못할 경우엔 0을 반환하도록 한다.</p>
<p><em>제한 사항</em></p>
<ul>
<li><em>1 ≤ topping의 길이 ≤ 1,000,000</em></li>
<li><em>1 ≤ topping의 원소 ≤ 10,000</em></li>
</ul>
<h4 id="문제풀이-1">문제풀이 1.</h4>
<p>형과 동생의 토핑을 중복없이 저장하기 위해서 set을 2개를 선언하고, 2중 for문으로 각 set에 index를 한개씩 늘려가면서 토핑을 추가해 주었다. 형의 set에 토핑이 모두 채워질 때 마다 동생과 형의 토핑의 개수를 비교하여 같다면 answer를 1증가시켜 주도록 코드를 짜 보았다.</p>
<pre><code>public int solution(int[] topping) {
    int answer = 0;
    // 공평하게 들어갔는지 확인할 set이 필요하다.
    // topping length 만큼 돌면서 작업을 진행한다.
    Set&lt;Integer&gt; set1 = new HashSet&lt;&gt;();
    Set&lt;Integer&gt; set2 = new HashSet&lt;&gt;();

    for (int i = 0; i &lt; topping.length - 1; i++) {
        set1.add(topping[i]);
        for (int j = i + 1; j &lt; topping.length; j++) {
            set2.add(topping[j]);
            if (set1.size() &lt; set2.size()) {
                break;
            }
        }

        if (set1.size() == set2.size()) {
            answer++;
        }
        set2 = new HashSet&lt;&gt;();
    }
    return answer;
}</code></pre><p>결과는 시간초과가 났다. 이중 for문과 한개의 for문이 끝날 때 마다 set을 다시 초기화 시켜주는 과정에서 시간초과가 난 것 같다.</p>
<h4 id="문제풀이-2">문제풀이 2.</h4>
<p>동생의 토핑은 set을 통해 저장하고, 형의 토핑은 map을 이용해 저장하는 방식으로 바꾸었다. map을 이용해 중복된 값은 key로 구분을 하고 몇개가 있는지 확인하고 해당 값을 value에서 차감시키는 방식으로 구현을 했다.</p>
<pre><code>public int solution(int[] topping) {
    int answer = 0;
    // 공평하게 들어갔는지 확인할 set이 필요하다.
    Set&lt;Integer&gt; set = new HashSet&lt;&gt;();
    Map&lt;Integer, Integer&gt; map = new HashMap&lt;&gt;();
    for (int i = 0; i &lt; topping.length; i++) {
        map.put(topping[i], map.getOrDefault(topping[i], 0) + 1);
    }

    for (int i = 0; i &lt; topping.length; i++) {
        set.add(topping[i]);

        if (map.get(topping[i]) == 1) {
            map.remove(topping[i]);
        } else {
            map.put(topping[i], map.get(topping[i]) - 1);
        }

        if (set.size() == map.size()) {
            answer++;
        }
    }
    return answer;
}</code></pre><h4 id="문제풀이2-결과">문제풀이2 결과</h4>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/289ee98d-daba-4890-95aa-45135f591f31/image.png" alt="문제풀이 결과"></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OS]Thread Synchronization]]></title>
            <link>https://velog.io/@nice-sso/OSThread-Synchronization</link>
            <guid>https://velog.io/@nice-sso/OSThread-Synchronization</guid>
            <pubDate>Wed, 28 Sep 2022 16:15:56 GMT</pubDate>
            <description><![CDATA[<p>📌 오늘은 스레드의 동기화에 대해 적어 보려고 한다.🧐 더 자세한 내용은 추후에 추가 할 수 있도록 해야겠다.</p>
<h3 id="thread-synchronization">Thread Synchronization</h3>
<h4 id="스레드-동기화">스레드 동기화</h4>
<p>멀티스레드를 이용하는 프로그램에서 스레드 2개 이상이 공유 데이터(stack을 제외한 메모리 영역)에 접근할 경우 다양한 문제가 생길 수 있는데, 이러한 문제를 해결하기 위한 일련의 작업들을 스레드 동기화라고 한다.
스레드 동기화를 위해서 임계영역과 락을 사용한다.</p>
<ul>
<li>임계영역(critical section): 임계영역으로 설정한 구역은 2개 이상의 스레드가 동시에 공유 리소스를 사용할 수 없게 하는 방식</li>
<li>락(lock): 락을 획득한 스레드에 대해서만 리소스를 사용하도록 허용하는 방식</li>
</ul>
<h4 id="동기화-기법">동기화 기법</h4>
<ul>
<li><p>뮤텍스(mutex): 공유 자원이나 임계영역에 대해 하나의 프로세스 또는 스레드만 접근 하도록 허용(동기화 대상이 한개)
<img src="https://velog.velcdn.com/images/nice-sso/post/1d2b2fde-9c64-4af0-8561-9bfedc97507c/image.png" alt="">
스레드들은 실행시간이 서로 겹치지 않게 단독으로 실행되며, 이를 상호배제(Mutual Exclution)라고 한다.</p>
</li>
<li><p>세마포어(semaphore): 공유 자원이나 임계영역에 대해 여러 프로세스나 스레드가 접근하는 것을 제한(동기화 대상이 한개 이상)
<img src="https://velog.velcdn.com/images/nice-sso/post/1ba147ce-09a8-4f5b-b5c9-69c904fe9fb5/image.png" alt="">
세마포어에 대한 연산은 처리 도중에 인터럽트 되어서는 안되며, 세마포어 알고리즘은 P(wait)연산과 V(signal)연산을 사용한다. V 연산은 큐에 대기 중인 프로세스를 깨우는 신호이며, P 연산은 프로세스들이 임계영역을 사용하려고 할 때 진입 여부를 결정하는 조작이다.</p>
</li>
<li><p>이벤트(event): 실행 순서를 동기화 한다.</p>
</li>
</ul>
<h4 id="synchronized를-이용한-동기화">synchronized를 이용한 동기화</h4>
<p>자바에서는 synchronized를 사용하여 임계영역을 지정한다.</p>
<ul>
<li>메소드 전체를 임계영역으로 지정: 메소드 선언 시 syncronized 키워드 추가<pre><code>public synchronized void Method() {    
</code></pre></li>
</ul>
<p>}</p>
<pre><code>- 특정 영역만 임계영역으로 지정: synchronized 키워드로 시작하는 블록 지정</code></pre><p>public void normalMethod() {<br>    synchronzied(/* 동기화할 객체 또는 클래스명 */) {    }
}</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[OS] Process와 Thread]]></title>
            <link>https://velog.io/@nice-sso/OS-Process-vs-Thread</link>
            <guid>https://velog.io/@nice-sso/OS-Process-vs-Thread</guid>
            <pubDate>Sun, 25 Sep 2022 13:29:36 GMT</pubDate>
            <description><![CDATA[<p>📌 오늘은 process와 thread의 각 개념과 차이점에 대해 적어보려고 한다. 기본적인 개념이지만 누군가에게 설명하기엔 헷갈리는 내용이기도 한 것 같아, 차례대로 정리해 보려고 한다.🧐</p>
<h3 id="process">Process</h3>
<p>process는 운영체제로부터 자원을 할당 받은 자원의 단위로써, 실행 중인 프로그램을 process라고 한다. 하드 디스크에서 프로그램이 실행되면 아래 사진과 같이 메모리에 프로그램 실행에 필요한 내용이 적재가 된다.
<img src="https://velog.velcdn.com/images/nice-sso/post/8b62356f-4a8b-4855-a3c0-d0fa0ba01f04/image.png" alt="">
프로세스가 메모리를 할당 받게 되면 code, data, heap, stack영역으로 나뉘게 된다. 또한, 프로세스 생성시 프로세스 정보를 담고 있는 PCB Block이 함께 만들어진다.</p>
<h4 id="메모리-영역">메모리 영역</h4>
<ul>
<li>code: 실행 명령을 포함하는 코드들</li>
<li>Data: static(정적) 변수, global(전역) 변수가 할당되어 있는 영역</li>
<li>heap: 동적 메모리 영역</li>
<li>stack: 지역변수, 매개변수, 반환 값 등 일시적인 데이터
<img src="https://velog.velcdn.com/images/nice-sso/post/c45589ee-e9c0-4546-99a4-3561c3dff7f9/image.png" alt=""></li>
</ul>
<h4 id="pcbprocess-control-block">PCB(Process Control Block)</h4>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/9e25c92e-e066-4158-9ae3-be2ff1eee1de/image.png" alt=""></p>
<ul>
<li>Pointer: 프로세스의 준비상태나 대기상태의 큐를 구현</li>
<li>Process State: 현재 프로세스의 상태(생성, 준비, 실행, 완료 등)</li>
<li>Process ID: 프로세스 고유번호</li>
<li>Program Counter: 다음에 실행될 명령어의 위치</li>
</ul>
<h4 id="process-state">Process State</h4>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/26f0fd5e-47e1-414c-af58-ed7fb9357ef9/image.png" alt=""></p>
<ul>
<li>new: 프로세스가 생성된 상태, 메모리에 올라와서 실행 준비를 마친 상태로 PCB도 같이 생성됨</li>
<li>ready: 생성된 프로세스가 CPU가 처리하길 기다리는 상태</li>
<li>running: 준비중이던 프로세스가 CPU를 얻어 작업을 수행</li>
<li>terminated: 프로세스 종료, PCB도 같이 같이 소멸</li>
</ul>
<h4 id="context-switching">Context Switching</h4>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/8e23d2e2-8ddd-436c-bcba-30e6c4f4f35a/image.png" alt="">
동시에 실행하고 싶은 프로세스 두개 P1과 P2가 있다고 할때, P1에 인터럽트 또는 시스템 콜이 발생하면 PCB1에 현재 상태를 저장하고 PCB2가 로드되면서 프로세스2가 실행되게 된다. 위와 같이 2개의 프로세스가 번갈아 가면서 CPU에 적재되는 과정을 context switching이라고 한다. context switching이 빈번하게 일어나면 성능저하의 원인이 된다. </p>
<h3 id="thread">Thread</h3>
<p>프로세스 안에서 실제로  작업을 수행하는 주체를 말한다. 모든 프로세스는 한개 이상의 스레드가 존재하여 작업을 수행하며, 2개 이상의 스레드를 가진 프로세스를 멀티 스레드 프로세스라고 한다.</p>
<h4 id="process와-thread의-차이점">process와 thread의 차이점</h4>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/6f251f8b-3046-4f8b-8edb-5eb22c996299/image.png" alt="">
thread는 process의 code, data, heap영역을 공유받으며 stack영역만 별도로 할당을 받는다. 즉, 자식 스레드는 부모 프로세스의 영역을 공유하면서 사용하게 된다.</p>
<h4 id="multi-process">Multi-Process</h4>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/c444f037-0737-41b6-bd9a-59c010915092/image.png" alt="">
멀티 프로세스는 하나의 프로그램을 여러개의 프로세스로 구성하여 각 프로세스가 하나의 작업을 처리한다.</p>
<ul>
<li>장점: 각 프로세스는 독립적으로 존재하기 때문에 하나의 프로세스에 문제가 생겨도 프로그램은 동작한다.</li>
<li>단점: 스케쥴링에 따른 context switching이 많아지면 성능 저하에 우려가 있다.</li>
</ul>
<h4 id="multi-thread">Multi-Thread</h4>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/5cac1884-30e7-46ba-9a54-187525bb0372/image.png" alt="">
멀티 스레드는 하나의 어플리케이션을 여러개의 스레드로 나누어 각 스레드가 하나의 작업을 처리하도록 하는 작업이다.</p>
<ul>
<li>장점: 시스템 자원을 공유하여 사용하기 때문에 자원 공유가 쉬우며, 처리 비용을 절감할 수 있다.</li>
<li>단점: 디버깅이 어려우며, 동기화 이슈가 발생할 수 있다. 하나의 쓰레드에 문제가 생길 시 전체 프로세스에 문제 발생</li>
</ul>
<p>마치면서, 다음 글은 스레드 동기화에 대해 다뤄보려고 한다.</p>
<p><em>이미지 참고 출처</em>
<em>1. <a href="https://charlezz.medium.com/process%EC%99%80-thread-%EC%9D%B4%EC%95%BC%EA%B8%B0-5b96d0d43e37">https://charlezz.medium.com/process%EC%99%80-thread-%EC%9D%B4%EC%95%BC%EA%B8%B0-5b96d0d43e37</a></em>
<em>2. <a href="https://forum.huawei.com/enterprise/en/context-switch-everything-you-need-to-know/thread/685119-895">https://forum.huawei.com/enterprise/en/context-switch-everything-you-need-to-know/thread/685119-895</a></em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Network] HTTP-요청 흐름/Http메소드/상태코드]]></title>
            <link>https://velog.io/@nice-sso/Network-HTTP</link>
            <guid>https://velog.io/@nice-sso/Network-HTTP</guid>
            <pubDate>Sat, 24 Sep 2022 16:03:49 GMT</pubDate>
            <description><![CDATA[<h3 id="http">HTTP</h3>
<h4 id="웹-브라우저의-요청-흐름">웹 브라우저의 요청 흐름</h4>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/af43f020-fd9f-41bf-a986-64ee45bfadde/image.png" alt="">
먼저 URL에 쓰인 각각의 의미를 먼저 살펴보자면, <a href="http://www.google.com%EC%9D%80">www.google.com은</a> 도메인명으로 DNS 서버에서 도메인명에 매칭되는 IP를 가진 서버를 응답해주는 형식이다.
443은 https 포트 번호를 의미하는데 생략해도 접속이 가능하다. 또한, http는 80포트를 사용한다.
/search는 리소스 경로(path)를 의미한다.
?뒤의 q=hello&amp;hi=ko는 쿼리 파라미터, 쿼리 스트링 등으로 불린다. 웹서버에 제공하는 파라미터, 문자 형태이다.</p>
<p>위와같이 요청을 하게 되면, 아래와 같은 HTTP 요청 메시지가 생성된다.
<img src="https://velog.velcdn.com/images/nice-sso/post/cae5e6a0-f949-49c2-8bbc-8cc97e88a3a7/image.png" alt="">
클라이언트가 HTTP요청을 하게 되면 서버는 아래와 같이 HTTP응답 메시지를 돌려준다. HTTP/1.1은 HTTP1.1 버전을 사용했다는 의미이며, 200은 상태코드로 정상적인 응답을 내려줄때 사용된다.
Content-Type부터 Content-Length는 HTTP 헤더를 나타낸다. 필요시 임의의 헤더는 추가가 가능하다.
html태그 안의 내용은 message body에 해당한다.</p>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/1d512489-cd28-4910-9883-2cf0a1037f3f/image.png" alt=""></p>
<p>클라이언-서버의 구조는 Request Response 구조에 해당하는데, 클라이언트가 보낸 상태를 보존 하느냐 않하느냐에 따라 Stateless, Stateful 상태로 나뉜다.</p>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/8552c23e-1527-426b-bbbb-f62e450f1352/image.png" alt=""></p>
<ol>
<li><p>Stateful(상태유지) 
Stateful같은 경우 항상 같은 서버가 유지 되어야 한다. 
만약 여러대의 분산서버가 있고 클라이언트가 서버1에 요청을 한 경우, 서버1에 장애가 나면 클라이언트는 응답을 받을 수 없는 단점이 존재한다. 이러한 점에서 상태 유지는 최소한만 사용하는 것이 좋다.</p>
</li>
<li><p>Stateless(무상태)
Stateless같은 경우 클라이언트가 서버에 요청을 보낼때, 필요한 정보를 그때마다 담아서 요청을 보내기 때문에 아무 서버나 호출해도 같은 응답을 받는다. 이렇게 보면 무상태만 쓰면 되는거 아니야?라고 생각이 들겠지만 모든 것을 무상태로 설계할 수는 없는 경우도 있다.</p>
</li>
</ol>
<h4 id="http-method">HTTP Method</h4>
<ol>
<li><p>GET: 리소스 조회
서버에 전달하고 싶은 데이터는 쿼리 파라미터(쿼리 스트링)을 통해서 전달한다. 메시지 바디를 사용해서 데이터를 전달할 수 있지만, 지원하지 않는 곳이 많아서 권장하지 않는다.</p>
</li>
<li><p>POST: 요청 데이터 처리, 주로 등록에 사용
메시지 바디를 통해서 서버로 요청 데이터를 전달한다. 주로 전달된 데이터로 신규 리소스를 등록하거나, 프로세스를 처리하는데 사용한다.</p>
</li>
<li><p>PUT: 리소스를 대체, 해당 리소스가 없으면 생성
POST는 서버가 URI를 지정한다면, PUT은 클라이언트가 리소스 위치를 알고 URI를 지정한다.</p>
</li>
<li><p>PATCH: 리소스 부분 변경
PUT은 리소스를 완전히 대체해버린다면, PATCH는 요청보내는 데이터만 변경을 하게 된다. 
<img src="https://velog.velcdn.com/images/nice-sso/post/ee141d2f-f107-422a-b6f3-165fe225c6b3/image.png" alt="">
예를 들어 위와 같은 요청을 보낼때 PUT같은 경우는 /members/100에 age=50을 보내게 되면 </p>
</li>
<li><p>DELETE: 리소스 삭제</p>
</li>
<li><p>HEAD: GET과 동일하지만 메시지 부분을 제외하고, 상태 줄과 헤더만 반환</p>
</li>
<li><p>OPTIONS: 대상 리소스에 대한 통신 가능 옵션(메서드)을 설명(주로 CORS에서 사용)</p>
</li>
<li><p>CONNECT: 대상 자원으로 식별되는 서버에 대한 터널을 설정</p>
</li>
</ol>
<h4 id="http-메서드의-속성">HTTP 메서드의 속성</h4>
<p><img src="blob:https://velog.io/f8b60367-3193-4c76-b91d-127adaca5010" alt="업로드중.."></p>
<p>메서드의 속성에는 안전, 멱등 ,캐시가능 여부가 있다.</p>
<ul>
<li>안전(Safe)이란 호출해도 리소스를 변경하지 않는 메소드를 말하며, 대표적으로 GET이 있다.</li>
<li>멱등(Idempotent)이란 여러번 호출해도 항상 결과가 똑같다는것을 의미한다. 멱등은 서버가 timeout 등으로 정상 응답을 주지 못할때, 클라이언트가 같은 요청을 다시 해도 되는가에 대한 판단 근거로 활용한다.</li>
<li>캐시가능(Cacheable)</li>
</ul>
<h4 id="status-code">Status Code</h4>
<ul>
<li>200번대: 요청 정상 처리</li>
<li>300번대: 요청을 완료하려면 추가 행동 필요</li>
<li>400번대: 클라이언트 오류</li>
<li>500번대: 서버 오류</li>
</ul>
<p><em>(참고)인프런의 김영한님 HTTP강의
<a href="https://www.inflearn.com/course/http-%EC%9B%B9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC/dashboard">https://www.inflearn.com/course/http-%EC%9B%B9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC/dashboard</a></em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Network] TCP/IP 동작순서]]></title>
            <link>https://velog.io/@nice-sso/Network-TCPIP</link>
            <guid>https://velog.io/@nice-sso/Network-TCPIP</guid>
            <pubDate>Sat, 17 Sep 2022 07:35:26 GMT</pubDate>
            <description><![CDATA[<p>📌 웹 개발자라면 필수로 알고 있어야 하는 개념인 TCP/IP와 HTTP에 대해 간단하게 기술하고, 기본적인 개념에 대해 정리하려고 한다.</p>
<h3 id="인터넷-네트워크">인터넷 네트워크</h3>
<h4 id="인터넷-통신">인터넷 통신</h4>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/44dcfddc-c55c-405c-be38-f7f257fd205a/image.png" alt=""></p>
<p>클라이언트와 서버가 서로 통신하기 위해서는 각자의 <strong>IP 주소</strong>가 필요하다. 왜냐하면, 인터넷 망에는 수많은 노드들이 있고 요청하고, 받는 주소가 지정되어 있어야만 정확한 통신이 가능하기 때문이다. 이때, <strong>패킷</strong>이라는 통신 단위로 데이터를 전달하게 되는데 해당 패킷에는 _출발지IP와 목적지IP, 전송 데이터의 정보_가 들어있다.</p>
<h4 id="ip-프로토콜의-한계">IP 프로토콜의 한계</h4>
<p>하지만 IP주소 만으로는 정확한 데이터를 주고 받는데 한계가 있다.</p>
<ol>
<li>비 연결성: 패킷을 받을 대상이 없거나 불능 상태여도 무조건 패킷 전송</li>
<li>비 신뢰성: 중간에 패킷이 사라지거나, 순서대로 오지 않는 경우</li>
<li>프로그램 구분: 같은 IP를 사용하는 서버에서 통신하는 application이 두개 이상인 경우</li>
</ol>
<p>이런 경우를 극복하기 위해 _TCP, UDP프로토콜_이 등장하게 된다.</p>
<h4 id="tcp-protocol">TCP Protocol</h4>
<p>먼저, 인터넷 프로토콜 스택의 4계층을 먼저 살펴본다.
<img src="https://velog.velcdn.com/images/nice-sso/post/68905fef-412c-43a6-be45-129ad8f203e4/image.png" alt="">
앞서 말한 IP는 인터넷 계층에 해당하며, TCP와 UDP는 전송계층에 해당한다.</p>
<p><img src="https://velog.velcdn.com/images/nice-sso/post/e7babbda-4bbf-42ba-9170-1f1129ed3ac5/image.png" alt="">
동작 순서를 살펴보면, 클라이언트의 프로그램이 Hello world라는 메시지를 생성하면 SOCKET LIBRARY를 통해서 전달이 되고 OS에선 TCP정보를 생성하고 메시지 데이터를 포함하게 된다. 그다음 TCP정보가 포함된 IP패킷을 생성하게 되고 LAN을 통해 서버로 전송하게 된다.
최종적으로 TCP/IP 패킷에 들어있는 정보는 아래와 같다.
<img src="https://velog.velcdn.com/images/nice-sso/post/7fc19bad-617c-478b-811f-d5bfbcc0b939/image.png" alt=""></p>
<h4 id="tcp-protocol의-특징">TCP Protocol의 특징</h4>
<p>TCP는 전송 제어 프로토콜(Transmission Control Protocol)이라고도 하며 특징은 아래와 같다.</p>
<ol>
<li><p>연결지향 - TCP 3 way handshake
<img src="https://velog.velcdn.com/images/nice-sso/post/6955cad4-a264-47aa-843a-c6804dcdbe37/image.png" alt="">
클라이언트가 접속 요청을 보내면 서버가 요청수락과 함께 데이터를 전송하고, 클라이언트는 잘 받았다고 다시 요청수락 응답을 보내는 과정</p>
</li>
<li><p>데이터 전달 보증
<img src="https://velog.velcdn.com/images/nice-sso/post/b69277d9-3793-4c70-9cba-163aaf83a372/image.png" alt="">
클라이언트가 데이터를 전송하면 서버는 데이터를 잘 받았다고 응답 메시지를 보내는 과정을 진행</p>
</li>
<li><p>순서 보장
<img src="https://velog.velcdn.com/images/nice-sso/post/acdbb8a8-ce7c-47bc-a940-993b1facd941/image.png" alt="">
클라이언트가 보낸 패킷이 서버에 순서가 정상적으로 도착하지 않았다면, 정상적으로 도착하지 않은 패킷부터 차례대로 다시 보내라고 응답하게 된다.
클라이언트가 요청하는 데이터의 크기가 크면 MTU 단위로 데이터를 쪼개는데 이 쪼갠 데이터를 세그먼트라고 하게 된다. 세그멘트를 송신할 때마다 쪼개진 데이터의 순서를 알 수 있도록 sequence 번호가 tcp헤더에 기록이 되는데, 이 헤더를 통해 패킷의 순서를 알 수 있다.</p>
</li>
</ol>
<h4 id="포트port">포트(port)</h4>
<p>포트란 같은 IP내에서 프로세스를 구분하기 위해 필요한 번호이다.</p>
<ul>
<li>FTP: 20, 21</li>
<li>TELNET: 23</li>
<li><strong>HTTP: 80</strong></li>
<li><strong>HTTPS: 443</strong></li>
</ul>
<p>글을 마치며, 학부생때 배운 내용이었지만 시간이 지나면서 점점 퇴화되고 있었는데 다시 개념을 다지며 기본적인 것이 가장 중요하다는 생각을 또 하게 된다. 그냥 책만 읽을땐 지루하고 크게 이해되지 않는 내용이었는데 실무를 조금 하고 나니 무슨 의미인지 더 크게 와닿는것 같다. 인강도 좋지만, 역시 심도 있게 공부하려면 책이 최고인 것 같다. :-)</p>
<p><em>(참고)인프런의 김영한님 HTTP강의
<a href="https://www.inflearn.com/course/http-%EC%9B%B9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC/dashboard">https://www.inflearn.com/course/http-%EC%9B%B9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC/dashboard</a></em></p>
]]></description>
        </item>
    </channel>
</rss>