<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>_hye.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Mon, 19 Feb 2024 03:05:42 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. _hye.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/_hye" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[백준) 사용한 문법 정리2]]></title>
            <link>https://velog.io/@_hye/%EB%B0%B1%EC%A4%80-%EC%82%AC%EC%9A%A9%ED%95%9C-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC2</link>
            <guid>https://velog.io/@_hye/%EB%B0%B1%EC%A4%80-%EC%82%AC%EC%9A%A9%ED%95%9C-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC2</guid>
            <pubDate>Mon, 19 Feb 2024 03:05:42 GMT</pubDate>
            <description><![CDATA[<h3> Wrapper class 메서드 </h3>

<p>(1) <code>String</code></p>
<ul>
<li>문자열 -&gt; 배열 변환<ul>
<li><code>char[] ary = msg.toCharArray()</code> : 문자열을 char타입의 배열로 변환 </li>
<li><code>String[] ary = msg.split(&quot;기준이 되는 문자열&quot;)</code> : 기준에 따라서 문자열을 나누어서 <strong>배열에 저장</strong></li>
</ul>
</li>
<li>문자열 비교 (반환값 boolean)<ul>
<li><code>msg.equalsIgnoreCase(&quot;abc&quot;)</code> : 대소문자를 구별하지 않고 <code>abc</code>가 문자열과 같은지를 반환</li>
<li><code>msg.startsWith(&quot;a&quot;)</code> : 문자열이 <code>a</code>로 시작하는지를 반환</li>
<li><code>msg.endWith(&quot;a&quot;)</code> : 문자열이 <code>a</code>로 끝나는지를 반환</li>
<li><code>msg.contains(&quot;a&quot;)</code> : 문자열이 <code>a</code>를 포함하고 있는지를 반환</li>
</ul>
</li>
</ul>
<ul>
<li><p>문자열 변환 후 반환</p>
<ul>
<li><code>msg.toUpperCase()</code> : 문자열 안의 소문자를 대분자로 변환</li>
<li><code>msg.toLowerCase()</code> : 문자열 안의 대문자를 소문자로 변환</li>
<li><code>msg.trim()</code> : 문자열 앞, 뒤의 공백을 제거
→ 중간의 공백은 제거하지 않는다.</li>
<li><code>msg.replace(&quot;abc&quot;, &quot;b&quot;)</code> : 문자열 안에서 <code>abc</code>를 <code>b</code>로 전부 변환 후 반환</li>
<li><code>msg.replaceAll(&quot;ab&quot;, &quot;b&quot;)</code> : 문자열 안에서 <code>ab</code>를 전부 <code>b</code>로 변환 후 반환
→ <a href="https://djusti.tistory.com/8"><code>replace</code>와 <code>replaceAll</code>은 거의 유사하지만 <code>replaceAll</code>에는 <strong>정규식(<code>[]</code>)이 사용가능하다.</strong></a></li>
</ul>
</li>
<li><p>문자열 반환</p>
<ul>
<li><code>msg.charAt(index)</code> : 문자열 안에서 index에 위치한 값을 반환</li>
<li><code>msg.substring(2,5)</code> : 문자열 안에서 index 2 ~ 5까지를 출력 (마지막 index는 -1로 지정)</li>
</ul>
</li>
</ul>
<p>(2) <code>Character</code> </p>
<ul>
<li><code>Character.toUpperCase(c)</code> : char타입의 변수 c를 대문자로 변환 후 반환
→ <code>Character.isUpperCase(c)</code>는 char타입의 변수 c가 대문자인지를 boolean타입으로 반환</li>
<li><code>Character.toLowerCase(c)</code> : char타입의 변수 c를 소문자로 변환 후 반환
→ <code>Character.isUpperCase(c)</code>는 char타입의 변수 c가 소문자인지를 boolean타입으로 반환</li>
</ul>
<h3 id="배열-정렬">배열 정렬</h3>
<ol>
<li>오름차순 정렬 : <strong>Arrays.sort(array)</strong></li>
</ol>
<pre><code class="language-java">    Arrays.sort(array);</code></pre>
<ol start="2">
<li>내림차순 정렬 : <strong>Collections.reverseOrder()</strong>
: Integer 과 같은 Wrapper class에만 사용가능하므로 유의해야 한다.</li>
</ol>
<pre><code class="language-java">    Arrays.sort(array, Collections.reverseOrder());</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준) 1193번]]></title>
            <link>https://velog.io/@_hye/%EB%B0%B1%EC%A4%80-1193%EB%B2%88</link>
            <guid>https://velog.io/@_hye/%EB%B0%B1%EC%A4%80-1193%EB%B2%88</guid>
            <pubDate>Mon, 19 Feb 2024 00:45:44 GMT</pubDate>
            <description><![CDATA[<h3> 처음 설계 </h3>

<p><img src="https://velog.velcdn.com/images/_hye/post/d3add03a-6f5d-42b2-bf5c-84384ccc1595/image.png" alt=""></p>
<p>: 문제를 보면 이런식으로 순서가 정해지고 있는데 여기서 규칙을 찾을 수 있다.</p>
<img src="https://velog.velcdn.com/images/_hye/post/5b845914-b450-4187-a013-c672914460a6/image.png" width="40%" height="n%">

<ul>
<li>한 줄에서 각각의 합은 모두 같다.</li>
<li>각 줄에서 요소의 개수는  각각의 합에서 -1을 뺀 값이다.</li>
<li>홀수의 경우 작은 수부터 시작하고 짝수의 경우 큰 수부터 시작한다.</li>
</ul>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.StringTokenizer;

public class Main {

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        int n = Integer.parseInt(br.readLine());

        int num = 2;
        String result = &quot;&quot;;
        int count = 0;

        while(count != n) {

            if(num % 2 == 0) {
                for(int j = num-1 ; j &gt;= 1 ; j--) {
                    int k = num - j;

                    result = j + &quot;/&quot; + k;
//                    bw.write(result + &quot;\n&quot;);
                    count++;

                    if(count == n) {
                        break;
                    }
                }
            }else {
                for(int j = 1 ; j &lt;= num -1 ; j++) {
                    int k = num - j;

                    result = j + &quot;/&quot; + k;
//                    bw.write(result + &quot;\n&quot;);
                    count++;

                    if(count == n) {
                        break;
                    }
                }
            }

            num++; 
//            bw.write(&quot;&gt;&gt;&gt;&quot; + num + &quot;\n&quot;);
        }

        bw.write(result);

        bw.close();
    }

}</code></pre>
<blockquote>
</blockquote>
<ol>
<li>사용자에게 n을 입력받는다.</li>
<li>각 줄의 합이 2부터 시작하기에(1번째 값인 1,1의 합이 2다.) 
num의 값, 초기값을 2로 지정해둔다.</li>
<li>연산한 횟수를 기록하는 count변수를 생성하였다.</li>
<li>num이 짝수라면 빼는 값이 큰 수부터 시작하기에 num-1을 초기값으로 잡고, num을 1까지 빼게 되므로 조건을 1보다 크거나 같게로 설정하였다.
ex) 4라면 3을 빼고 나온 값을 k에 담는다.</li>
<li>이렇게 뺀 값을 출력형태로 result에 저장한다.</li>
<li>count를 증가시키는데 이때 count가 n과 같다면 반복문을 종료하고
result를 출력한다.</li>
</ol>
<p>이렇게 했을 때 시간초과로 오류가 발생했다..</p>
<h3> 다음 설계 </h3>

<pre><code class="language-java">import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.StringTokenizer;

public class Main {

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        int n = Integer.parseInt(br.readLine());
        String result = &quot;&quot;;

        int i = 0;
        int count = 0;

        while(true) {

            if(n - i &gt; 0){
                n -= i;
                count++; i++;
                continue;
            }

            break;
        }

        int num = count+1;

        if(num % 2 == 0) {
            for(int j = num-1 , p = 0; p &lt; n ; j--, p++) {
                int k = num - j;

                result = j + &quot;/&quot; + k;
            }
        }else {
            for(int j = 1 , p = 0; p &lt; n ; j++, p++) {
                int k = num - j;

                result = j + &quot;/&quot; + k;

            }    

        }

        bw.write(result);

        bw.close();
    }

}</code></pre>
<p>시간을 줄이기 위해 while문과 for문을 분리하기로 했다.</p>
<blockquote>
<p>while문 : n번째에 해당하는 분자와 분모의 합(<code>num</code>)이 무엇인지 찾아내는 구문
→ 각 줄이 분자와 분모의 합의 -1개수만큼 존재하므로 
n에 각 줄의 개수를 빼면서 n이 몇 번째 줄에 속하는지를 구해낸다.
ex) 7번째라면 -0 -1 -2 -3 을 통해 count는 4, 즉 n은 4번째 줄에 속하게 되고 분자와 분모의 합(<code>num</code>)은 5이다.</p>
</blockquote>
<blockquote>
<p>for문 : num이 짝수인지 홀수인지에 따라 빼는 값을 다르게 한다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준) 2480번]]></title>
            <link>https://velog.io/@_hye/%EB%B0%B1%EC%A4%80-2480%EB%B2%88</link>
            <guid>https://velog.io/@_hye/%EB%B0%B1%EC%A4%80-2480%EB%B2%88</guid>
            <pubDate>Tue, 06 Feb 2024 01:32:47 GMT</pubDate>
            <description><![CDATA[<h3>2480번 : 주사위 세개</h3>

<pre><code class="language-java">import java.util.ArrayList;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {

        Scanner scan = new Scanner(System.in);

        long[] num = new long[3];
        long money = 0; long count = 0; 
        long same_num = 0; long max_num = 0;

        num[0] = scan.nextLong();
        num[1] = scan.nextLong();
        num[2] = scan.nextLong();

        for(int i=0 ; i &lt; num.size() ; i++) {

            for(int j = i+1 ; j &lt; num.size(); j++) {

                if(num[i] == num[j]) {
                    count++;
                    same_num = num[i];
                }
            }
        }

        System.out.println(same_num);

        if(count == 3) {
            money = 10000 + num[0] * 1000;
        }else if(count == 2) {
            money = 1000 + same_num * 100;
        }else {

             for( long in : num) {
                 if(in &gt; max_num) 
                     max_num = in;
             }

             money = max_num * 100;

         }

         System.out.println(money);


    }

}</code></pre>
<ul>
<li><p>처음 구현하고자 했던 것
숫자 3개가 있을 때 
1,2를 비교 / 1,3을 비교 / 2,3을 비교한 후
같을 때마다 count에 +1을 해주었다. <br>
→ 이를 위해 이중루프 안의 j를 i+1의 값으로 설정했다.</p>
<p>i | j |
|:---:|:---:|
1 |  2,3 
2 | 3
3 | X|
→ 문제점 : <code>2 2 6</code>으로 들어올 때 count의 값이 1로 나오게 된다. <br>
→ 해결 : 배열이 아니라 ArrayList로 구현하여 같은 숫자가 나올 경우 list안에서 같은 숫자를 다 뺀후 list의 길이에 따라서 판별한다.</p>
<p>input | num.size() |
|:---:|:---:|
3 3 3 | 0 
2 2 6 | 1
1 2 5 | 3 |</p>
</li>
</ul>
<pre><code class="language-java">import java.util.ArrayList;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {

        Scanner scan = new Scanner(System.in);

        ArrayList&lt;Long&gt; num = new ArrayList&lt;Long&gt;();
        long money = 0; 
        long same_num = 0; long max_num = 0;

        num.add(scan.nextLong());
        num.add(scan.nextLong());
        num.add(scan.nextLong());

        for(int i=0 ; i &lt; num.size() ; i++) {

            for(int j = i+1 ; j &lt; num.size(); j++) {

                if((num.get(i) == num.get(j)) ) {
                    same_num = num.get(i);
                }
            }
        }

        while(num.remove(Long.valueOf(same_num))) {}


        if(num.size() == 3) {

            for( long in : num) {
                if(in &gt; max_num) 
                    max_num = in;
            }

            money = max_num * 100;

        }else if(num.size() == 1) {

            money = 1000 + same_num * 100;

        }else {

            money = 10000 +same_num * 1000;        
        }

        System.out.println(money);

    }

}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준) 사용한 문법 정리]]></title>
            <link>https://velog.io/@_hye/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@_hye/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 06 Feb 2024 01:28:00 GMT</pubDate>
            <description><![CDATA[<h3>BufferedReader - BufferedWriter</h3>

<p>* Stream : 연속적으로 <u>단방향</u>으로 흘러가는 것 
→ 데이터가 지나가는 통로 
→ 단방향이기에 <strong>하나의 stream으로 입출력이 안된다.</strong></p>
<ul>
<li>java <ul>
<li><code>OutputStream</code> : 데이터를 보낼 때</li>
<li><code>InputStream</code> : 데이터를 수신할 때<br>

</li>
</ul>
</li>
</ul>
<p>(1) BufferedReader : 텍스트를 <strong>버퍼링</strong>
→ 한번에 하나의 문자가 아니라 <strong>여러개의 문자를 한번에 읽어서 버퍼에 저장</strong>한다.
* 버퍼링 : data를 모아두었다가 한번에 전송하는 것.</p>
<pre><code class="language-java">BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

char[] input = (br.readLine()).toCharArray();
int input = Integer.parseInt(br.readLine());</code></pre>
<ul>
<li><code>InputStreamReader</code> : 입력 스트림을 통해서 들어온 것을 byte를 문자열로 전환</li>
<li><code>System.in</code> : terminal로 들어오는 사용자의 입력</li>
<li><code>br.readLine()</code> : 입력에서 한 줄(\n)으로 끝나는 데이터를 읽는다.</li>
</ul>
<br>

<p>(2) BufferedWriter : 텍스트를 <strong>버퍼링</strong></p>
<pre><code class="language-java">BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

bw.write(time + &quot; &quot;);
bw.flush();
bw.close();</code></pre>
<ul>
<li><code>bw.write()</code> : 값을 출력한다.
→ 문자열을 같이 출력해주지 않으면 외계어가 출력되는 경우가 있다.</li>
<li><code>bw.flush</code> : 버퍼에 남은 것을 모두 출력한다.</li>
<li><code>bw.close</code> : BufferedWriter을 닫는다.</li>
</ul>
<p>(3) 다른 Writer</p>
<ul>
<li><code>객체.getOutputStream()</code> : 객체가 가지고 있는 OutputStream을 반환한다. </li>
<li><code>PrintWriter</code> : 텍스트 출력 기반 mapper
→ 텍스트 데이터를 쉽게 사용할 수 있게 관리한다.
→ 인자로 OutputStream을 받는다.</li>
</ul>
<p>(4) 입력의 끝 확인하기</p>
<pre><code class="language-java">while((input = br.readLine()) != null) {
    System.out.println(input);
}</code></pre>
<hr>
<h3>StringTokenizer</h3>

<pre><code class="language-java">StringTokenizer st = new StringTokenizer(br.readLine(), &quot; &quot;);

st.nextToken();        // 다음 token값을 반환
st.hasMoreTokens();    // 다음 token값이 있다면 true, 없다면 false를 반환
st.countTokens()    // token의 개수를 반환</code></pre>
<p><img src="https://velog.velcdn.com/images/_hye/post/21f4b434-3ba6-461f-b883-89cfdd3fb571/image.png" alt=""> </p>
<ul>
<li><p>StringTokenizer은 한 줄을 입력받을 때마다 생성해주어야한다.</p>
</li>
<li><p>숫자 하나만 받을 경우에는 StringTokenizer을 사용하지 않는 것이 좋다.</p>
</li>
<li><p>공백 : split은 문자열의 시작에 오는 공백도 배열에 포함시키지만 <strong>StringTokenizer는 문자열의 시작에 오는 공백은 생략한다.</strong></p>
</li>
</ul>
<hr>
<h3>ArrayList</h3>


<ul>
<li><p><a href="https://hianna.tistory.com/564">ArrayList의 remove메서드</a></p>
<ul>
<li><code>num.remove(1)</code> : 첫번째 index의 값을 제거한다.</li>
<li><code>num.remove(Long.valueOf(same_num))</code> : <strong>Object 객체</strong>를 매개변수로 받으며 <strong>처음 나온</strong> same_num을 제거한다.
→ 여기에서는 <code>Long.valueOf()</code>를 통해 Long 객체로 변환해주었다.</li>
<li><code>while(num.remove(Long.valueOf(same_num))) {}</code>를 통해 list안에 있는 <strong>모든 same_num</strong>을 제거한다.</li>
</ul>
</li>
<li><p>arrayList도 foreach가능함.</p>
</li>
<li><p>최대 최소</p>
<ul>
<li><code>Collections.max(arrayList)</code> : 최댓값 반환</li>
<li><code>Collections.min(arrayList)</code> : 최소값 반환</li>
</ul>
</li>
<li><p>변수 포함, 확인</p>
<ul>
<li><code>arrayList.contains(변수)</code> : ArrayList안에 변수값을 포함하는지 true, false를 반환한다. </li>
<li><code>Collections.frequency(arrayList, Integer.valueOf(in))</code> : ArrayList안에 이 값이 몇 개 있는지를 반환
→ Collectons.frequency는 시간복잡도를 높으므로 사용하지 않는 것이 좋다.</li>
<li><code>arrayList.indexOf(Integer.valueOf(max_num))</code> : 입력받은 값의 index를 반환</li>
</ul>
</li>
<li><p>정렬</p>
<ul>
<li><code>Collections.sort(arrayList)</code> : ArrayList를 오름차순 정렬</li>
<li><code>Collections.sort(arrayList, Collections.reverseOrder())</code> : ArrayList를 내림차순 정렬</li>
</ul>
</li>
<li><p>배열 변환</p>
<pre><code class="language-java">// array -&gt; list
ArrayList&lt;String&gt; list = new ArrayList&lt;String&gt;(Arrays.asList(ary));
</code></pre>
</li>
</ul>
<p>// list -&gt; array
list.toArray();</p>
<pre><code>
---

&lt;h3&gt;형 변환&lt;/h3&gt;

- `String` - `int`
```java
// String -&gt; int
int in = Integer.parseInt(String);

// int -&gt; String
String st = Integer.toString(int);</code></pre><ul>
<li><code>String</code> - <code>char</code> <pre><code class="language-java">//  char -&gt; String
char[] ch = string.toCharArray();
string.charAt(index);    // String중에서 index의 값을 char로 반환
String.indexOf(&quot;a&quot;);     // String에서 a가 위치한 index값을 반환
</code></pre>
</li>
</ul>
<p>// String -&gt; char
char a = String.valueOf(char);</p>
<p>// String -&gt; char[] : 다른 Wrapper Class는 배열 변환이 불가능하다.
String s = String.valueOf(charAry);</p>
<pre><code>
- `int` - `double` 
```java
Double.valueOf(int);</code></pre><p>: 같은 맥락으로 list에서 remove를 사용하기 위해 
<code>while(num.remove(Long.valueOf(same_num))) {}</code> 의 경우가 있다.</p>
<ul>
<li><code>char</code> - <code>int</code> <pre><code class="language-java">// char -&gt; int
int i = (int) c;    (명시적 형변환?)
int i = c;            (묵시적 형변환)
</code></pre>
</li>
</ul>
<p>// int -&gt; char
char c = (char) i;
char c = i;</p>
<pre><code>
---

코드를 작성할 때 꼭 알고리즘을 찾으려 하지말고 단순무식하게 짜도 된다.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Socket) Web Socket으로 실시간 채팅 구현하기2]]></title>
            <link>https://velog.io/@_hye/Socket-Web-Socket</link>
            <guid>https://velog.io/@_hye/Socket-Web-Socket</guid>
            <pubDate>Fri, 02 Feb 2024 00:19:40 GMT</pubDate>
            <description><![CDATA[<h2 id="web-socket---client-환경-세팅">Web Socket - Client 환경 세팅</h2>
<p>(1) 모듈 생성과 client socket 생성 </p>
<pre><code>&lt;script&gt;
  import {onMount} from &#39;svelte&#39;;
  import axios from &#39;axios&#39;;
  import io from &#39;socket.io-client&#39;;

  let messages = [];
  let newMessage = &#39;&#39;; </code></pre><blockquote>
<p>라이브러리 설명</p>
</blockquote>
<ul>
<li><code>{onMount}</code> : svelte에서 페이지가 로딩되면 먼저 실행되는 함수</li>
<li><code>axios</code> :  Promise 기반 HTTP 클라이언트 라이브러리
<a href="https://velog.io/@eunbinn/Axios-vs-Fetch">https://velog.io/@eunbinn/Axios-vs-Fetch</a>
fetch vs axios 라이브러리 설명</li>
<li>) <code>Promise</code> : 통신 중 client에 response가 도착하기 전에 먼저 도착하는 가짜? response
<a href="https://javascript.info/promise-basics">https://javascript.info/promise-basics</a></li>
<li><code>io</code> : socket.io(server의 socket라이브러리)를 client와 연결하기 위한 라이브러리</li>
</ul>
<blockquote>
<p>변수 설명</p>
</blockquote>
<ul>
<li><code>messages</code> : client간 전송될 메시지를 담을 배열</li>
<li><code>newMessage</code> : client가 전송할 메시지</li>
</ul>
<p>(2) connection 📞</p>
<pre><code>const socket = io(&quot;http://localhost:8000&quot;);</code></pre><p>: server의 socket과 연결하는 부분</p>
<hr>
<h2 id="web-socket---client-기능-구현">Web Socket - client 기능 구현</h2>
<p>(1) 초기 메시지 목록 가져오기 - RestAPI 호출 💬 </p>
<pre><code class="language-js">  onMount(async() =&gt;{
    // 이 부분은 RestAPI 호출하는 부분
    const response = await axios(&#39;http://localhost:8000/messages&#39;);
    console.log(response.data[0]);
    messages = [...messages,...response.data];
})</code></pre>
<blockquote>
<p>1 → <code>onMount</code> : 아래의 내용들을 가장 먼저 실행
<code>async ~ await</code> → promise값이 아니라 response를 받을 때까지 기다린다.
2 → server의 /messages 엔드포인트를 호출해 response를 받는다.</p>
</blockquote>
<pre><code class="language-js">// 서버의 /messages 엔드포인트
app.get(&#39;/messages&#39;,(req,res)=&gt;{
    console.log(&quot;이전 메시지들을 불러옵니다.&quot;)
    res.json(messages);
})</code></pre>
<p>3 → console에 json형태로 온 response안에서 데이터가 있는 영역 중 첫번째만 띄운다.
4 → client에 있던 <code>messages</code> 배열에 서버가 보낸 메시지 배열을 합친다.
+) 배열끼리 합칠 때 <code>배열 = [... 배열, ...합칠 배열]</code>을 이용하면 쉽게 배열을 합칠 수 있다.</p>
<p>(2) 서버에서 메시지 수신하기 📩</p>
<pre><code class="language-js">    //서버에서 호출하는 부분
    socket.on(&quot;message&quot;,(message)=&gt;{
      messages =[...messages,message];
      newMessage = &#39;&#39;;
      console.log(messages);
    })
  })  </code></pre>
<blockquote>
<p>1 → server에서 <code>message</code> 이벤트를 호출한다면 
response로 message를 받고 
2 → client에 있던 <code>messages</code> 배열에 서버에서 보낸 message를 넣는다.
3 → 사용자가 입력했었다면 <code>newMessage</code>변수를 초기화한다.
4 → console에 서버에서 보낸 message를 띄운다.</p>
</blockquote>
<p>(3) 서버에 메시지 전송하기 💌</p>
<pre><code class="language-js">function sendMessage(){
    const message = {
      text:newMessage,
      timestamp : new Date().toISOString(),
    }

  //axios.post(&#39;http://localhost:8000/post_messages&#39;,message);    //RestAPI 호출
    socket.emit(&#39;message&#39;,message);     // socket.io 호출

  }
&lt;/script&gt;</code></pre>
<blockquote>
<p>1 → sendMessage()함수 정의 (Js문법)
2 → <code>message</code> dictionary에 사용자가 입력한 <code>newMessage</code> 와 지금 시간을 저장한다. 이 <code>message</code>는 서버에 보낼 dictionary이다.
5 → ReatAPI를 통해 서버에 정의되어있는 <code>post_message</code> 엔드포인트를 호출한다.
6 → socket을 통해 서버에 정의되어있는 <code>message</code> 이벤트를 호출한다.</p>
</blockquote>
<pre><code class="language-svelte">&lt;input bind:value={newMessage} on:keydown=&quot;{e=&gt; e.key===&#39;Enter&#39; &amp;&amp; sendMessage()}&quot;/&gt;
&lt;button on:click={sendMessage}&gt;Send&lt;/button&gt;

&lt;ul&gt;
{#each messages as message (message.timestamp)}
&lt;li&gt;{new Date(message.timestamp).toLocaleTimeString()}: {message.text}&lt;/li&gt;
{/each}
&lt;/ul&gt;</code></pre>
<input bind:value={newMessage} on:keydown="{e=> e.key==='Enter' && sendMessage()}"/>
<button on:click={sendMessage}>Send</button>

<blockquote>
<p>1 → 사용자가 입력한 값을 <code>newMessage</code>에 저장한다.
2 → Send버튼을 클릭했을 때 위에서 정의한 sendMessage 함수를 호출한다.
3 → <code>message</code> 배열에 있는 모든 메시지와 시간을
4 → 화면에 출력</p>
</blockquote>
<hr>
<h2 id="시현영상">시현영상</h2>
<p><img src="https://velog.velcdn.com/images/_hye/post/084a55cd-fff3-4cfd-89ce-6e89d0120a38/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Socket) Web Socket으로 실시간 채팅 구현하기1]]></title>
            <link>https://velog.io/@_hye/Socket-Web-Socket%EC%9C%BC%EB%A1%9C-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%B1%84%ED%8C%85-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B01</link>
            <guid>https://velog.io/@_hye/Socket-Web-Socket%EC%9C%BC%EB%A1%9C-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%B1%84%ED%8C%85-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B01</guid>
            <pubDate>Thu, 01 Feb 2024 03:21:29 GMT</pubDate>
            <description><![CDATA[<h3>Web Socket</h3>

<p>(1) 환경</p>
<ul>
<li>server : local에서 <code>Node.js</code></li>
<li>client : local에서 <code>svelte</code></li>
</ul>
<p>(2) 사용 라이브러리</p>
<ul>
<li><code>socket.io</code> 또는 <code>ws</code> 라이브러리중에서 <code>socket.io</code> 사용
→ 실시간 채팅의 경우 <code>socket.io</code>가 더 적합하다.</li>
<li><code>net</code> </li>
</ul>
<p>(3) 환경 setting</p>
<ul>
<li><p>svelte : <code>npx dgit sveltejs/template svelte-app</code></p>
<ul>
<li><p><code>npx</code> : npm패키지 실행 도구, npm이 전역으로 설치되어 있지 않아도 npm이 바로 실행될 수 있게 한다.</p>
</li>
<li><p>) <code>npm install -g</code> : npm을 전역으로 설치하는 명령어</p>
</li>
<li><p><code>dgit</code> : github같은 git 저장소에서 프로젝트의 초기템플릿을 복사하는 도구
→ svelte의 공식템플릿인 <code>sveltejs/template</code>를 복사한다.</p>
</li>
<li><p><code>svelte-app</code> : 만들어질 directory 이름</p>
</li>
</ul>
</li>
<li><p>svelte 템플릿: 
<code>npm create vite@latest 프로젝트 이름 -- --template 프레임워크 이름</code>
<code>npm create vite@latest frontend -- --template svelte</code></p>
</li>
</ul>
<ul>
<li><code>npm create</code> : 프로젝트 스캐폴딩을 위한 명령어</li>
<li>) 프로젝트 스캐폴딩 : 프로젝트를 시작할 때 기본적인 구조와 설정을 자동으로 생성해주는 과정</li>
<li><code>vite@latest</code> : 최신버전의 vite 패키지를 가져온다.</li>
<li>) vite : 빌드 도구, 프레임워크의 초기설정과 템플릿을 가져와주는 도구</li>
</ul>
<ul>
<li>node.js 설치
<a href="https://nodejs.org/en/download">https://nodejs.org/en/download</a>
LTS버전을 설치하며 따로 설정할 것은 없고 전부 기본세팅으로 설치한다.</li>
</ul>
<p>(4) 라이브러리</p>
<ul>
<li>node.js - <code>express</code> : Web Server 프레임워크 라이브러리</li>
<li>node.js - <code>socket.io</code> : Web Socket 라이브러리
<a href="https://socket.io/">https://socket.io/</a> - <code>socket.io</code> 공식 사이트로써 기본적인 활용방법이 나와있다.</li>
</ul>
<hr>
<h3>Web Socket - Server 환경 세팅</h3>

<p>(1) 모듈 호출과 서버 socket 생성</p>
<pre><code class="language-js">const express = require(&#39;express&#39;);
const http = require(&#39;http&#39;);
const socketIo = require(&#39;socket.io&#39;);
const bodyParser= require(&#39;body-parser&#39;);
const cors = require(&#39;cors&#39;);

const app = express();
app.use(bodyParser.json());     // body 부분 처리
app.use(cors());        // cors 부분 에러 해결, 모든 도메인에서의 요청 허용
const server = http.createServer(app);
const io = socketIo(server, {
    cors: {
      origin: &quot;*&quot;,
      methods: [&quot;GET&quot;, &quot;POST&quot;]
    }
});</code></pre>
<blockquote>
<p>라이브러리 설명</p>
</blockquote>
<ul>
<li><code>body-parser</code> : response를 json형태로 변환하기 위한 라이브러리</li>
<li><code>cors</code> : cors 에러를 해결하기 위한 라이브러리</li>
</ul>
<blockquote>
<p>함수 설명</p>
</blockquote>
<ul>
<li><code>require</code> : 다른 파일이나 모듈을 가져오는 함수</li>
<li><code>createServer</code> : 매개변수로 <code>client의 request에 따라 실행되는 함수</code>를 받는다. 즉 <strong>request handler</strong>이다.</li>
<li><code>socketIO</code> : 매개변수로 http server 객체를 받는다.</li>
<li><blockquote>
<p>Web Socket은 초기 연결을 http로 수립한 후 해당 연결을 업그레이드하여 연결을 유지하며 데이터를 교환한다.</p>
</blockquote>
</li>
</ul>
<blockquote>
<p>변수 설명</p>
</blockquote>
<ul>
<li><code>app</code> : Application이라는 객체, get, post, put, delete같은 HTTP 요청을 처리하는 메서드를 가지고 있다.
→ get, post, put, delete는 client가 request하는 것이기에 <code>createServer</code>의 매개변수로 사용된다. </li>
<li><code>server</code> : Application 객체를 기반으로 하는 <strong>http server</strong></li>
<li><code>io</code> : serverSocket</li>
</ul>
<blockquote>
<p>cors 문제 해결</p>
</blockquote>
<pre><code class="language-js">const io = socketIo(server, {
    cors: {
      origin: &quot;*&quot;,
      methods: [&quot;GET&quot;, &quot;POST&quot;]
    }
});</code></pre>
<ul>
<li><code>origin</code> : 모든 ip주소에서의 요청 허용</li>
<li><code>methods</code> : <code>GET</code>, <code>POST</code> 요청만 허용</li>
</ul>
<p>(2) Connection</p>
<pre><code class="language-js">io.on(&#39;connection&#39;,(socket) =&gt; {
    console.log(&#39;New Client Connected&#39;);

    socket.on(&#39;disconnect&#39;,() =&gt;{
        console.log(&#39;Client disconnect&#39;);
    });
});

server.listen(8000,() =&gt; console.log(&#39;Listening on port 8000&#39;));</code></pre>
<ul>
<li><code>on(이벤트 handler, (resquest) =&gt; {내부로직});</code> 
: client에서 <code>io.이벤트</code>메서드를 호출하면 호출할 때 함께 전송되어진 <code>request</code>를 이용해 내부로직을 처리한다.</li>
</ul>
<blockquote>
<p>→ <code>connection</code> : client에서 <code>io.connection</code>메서드를 통해 socket연결을 요청하면 실행되는 이벤트 handler
→ <code>(socket)</code> : request, 함수 호출을 통해 생성되는 connectionSocket이다.
→ 화살표함수에서 파라미터의 위치에 따라 request, response가 결정된다. 파라미터가 1개일 경우 request이다.</p>
</blockquote>
<blockquote>
<p><code>socket.on(&#39;disconnect&#39;,() =&gt;{ console.log(&#39;Client disconnect&#39;);});</code> 
→ 위의 response로 들어온 <code>socket</code>을 통해 
<code>disconnect</code>요청이 들어온다면 
<code>()</code> : 따로 request와 response를 관리하지 않고 
console에 log로 &#39;Client disconnect&#39;를 출력하겠다.</p>
</blockquote>
<ul>
<li><code>server.listen(포트번호, () =&gt; 내부로직);</code> : 포트번호에서 server가 응답을 <strong>대기중</strong>이다.</li>
</ul>
<hr>
<h3>Web Socket - Server 기능 구현</h3>

<p>(1) 이전 메시지들 불러오기 </p>
<pre><code class="language-js">let messages = [{text: &#39;어서오세요!&#39;, timestamp : new Date().toISOString()}];  
//메세지들을 저장할 배열, 처음에는 어서오세요!와 최초 사용자가 들어온 시간이 출력된다.

app.get(&#39;/messages&#39;,(req,res)=&gt;{
    console.log(&quot;이전 메시지들을 불러옵니다.&quot;)
    res.json(messages);
})</code></pre>
<ul>
<li><code>app.HTTP메서드 ( &#39;url&#39; , (request, response) =&gt; {내부로직});</code>
: client가 get,post,put,delete의 HTTP메서드를 <code>url</code>을 통해서
<code>request</code>와 함께 요청을 보낸다면 <code>response</code>를 정의해서 내부로직을 처리한다.</li>
</ul>
<blockquote>
<p>1 → <code>app</code> 은 HTTP요청을 처리하는 객체로써<br>→ client가 <code>get</code>의 <code>/messages</code> 요청을 보내면 
→<code>(req,res)</code> : request를 받고 response를 생성해서
2 → console에 &quot;이전 메시지들을 불러옵니다.&quot;라는 메시지를 출력한다.
3 → response로 <code>json(messages)</code>, 
위의 이전 메시지들을 담아두는 배열(messages)를 <code>json</code>형태로 전송한다.</p>
</blockquote>
<br>

<p>(2) 메시지 전송 (RestAPI or socket)
: server의 기능구현은 RestAPI 엔드포인트와 같은 HTTP기반응답과
socket.io로만 구현했다.</p>
<ul>
<li><p>RestAPI </p>
<pre><code class="language-js">app.post(&#39;/post_messages&#39;,(req,res)=&gt;{
  const reqMessage = req.body;
  messages.push(reqMessage);

  io.emit(&#39;message&#39;,reqMessage);

  res.status(201).end();
 })</code></pre>
</li>
</ul>
<blockquote>
<p>1 → client에서 <code>/post_message</code>라는 요청을 보내면
 → <code>(req,res)</code> : request를 받고 response를 생성해서
   2 → reqest의 body안의 내용을 <code>reqMessage</code>라는 변수에 저장한다.
   3 → server가 가지고 있는 이전 메시지들을 담는 배열 messages에 client가 보낸 reqMessage를 저장한다.
   4 → client에서 정의된 <code>message</code>메서드를 reqMessage와 함께 호출시킨다.
     이는 한 client가 보낸 메시지를 다른 client에게 모두 보내기 위해서이다.
   5 → <code>res.status(201).end()</code></p>
</blockquote>
<blockquote>
<p>res 메서드</p>
</blockquote>
<ul>
<li><code>res.end()</code> : Express.js에서 응답 process를 종료하는 메서드, client에게 어떤 data도 보내지 않고 단순히 응답 process를 종료한다.
ex) post메서드에서 data를 보낼 필요 없이 응답을 종료해야 할 때</li>
<li><code>res.status()</code> : 인자안의 값을 status code로 설정한다.</li>
<li><code>res.json()</code> : 인자안의 값을 json형태로 변환 후 client에게 전달한다.</li>
<li><code>res.send()</code> : 인자안에 다양한 타입(문자열, 객체, 배열 ...)을 받을 수 있고 client에게 전달한다.
→ <code>res.json()</code>과 <code>res.send()</code>는 내부적으로 <code>res.end()</code>를 포함하고 있다.</li>
</ul>
<ul>
<li>socket </li>
</ul>
<pre><code class="language-js">socket.on(&#39;message&#39;,(reqMessage) =&gt;{
    messages.push(reqMessage);
    io.emit(&#39;message&#39;,reqMessage);  
    // 모든 사용자에게 전송된 메시지들을 보내기 위해 
});</code></pre>
<ul>
<li><code>socket.on(&#39;url&#39;, (request) =&gt; {내부로직});</code>
: client가 url을 통해 request를 보내온다면 내부로직을 처리한다.</li>
</ul>
<blockquote>
<p>1 → client가 <code>message</code> url을 <code>reqMessage</code>와 함께 요청한다면
2 → server가 가지고 있는 이전 메시지들을 담는 배열 messages에 client가 보낸 reqMessage를 저장한다.
3 → client에서 정의되어있는 <code>message</code>이벤트를 호출하며 reqMessage를 같이 보낸다.</p>
</blockquote>
<blockquote>
<p><code>on</code>은 이벤트를 정의하는 것이고, <code>emit</code>은 이벤트를 호출하는 것</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Socket) Socket Programming]]></title>
            <link>https://velog.io/@_hye/Socket-Socket-Programming</link>
            <guid>https://velog.io/@_hye/Socket-Socket-Programming</guid>
            <pubDate>Tue, 30 Jan 2024 08:36:57 GMT</pubDate>
            <description><![CDATA[<h3>Socket programming</h3>

<p>: Socket을 이용한 통신 프로그래밍(TCP기준)
→ C언어를 기준으로 작성되었으나 언어마다 함수의 이름만 다를 뿐 전체적인 흐름과 기능은 같다.</p>
<p><img src="https://velog.velcdn.com/images/_hye/post/4d2bc4ce-4ce0-4053-ba59-f87c906bde97/image.png" alt=""></p>
<hr>
<h3>Socket</h3>

<p><code>socket (int domain, int type, int protocol)</code> 
: socket 생성
<br></p>
<p>domain 옵션|AF_UNIX | AF_INET | AF_INET6
|:---:|:---:|:---:|:---:| 
뜻| process끼리 통신 |IPv4|IPv6
<br></p>
<p>type 옵션|SOCK_STREAM | SOCK_DGRAM | SOCK_RAW
|:---:|:---:|:---:|:---:| 
뜻| TCP |UDP|IP계층바로사용
<br></p>
<p>protocol 옵션|IPPROTO_TCP | IPPROTO_UDP | 0
|:---:|:---:|:---:|:---:| 
뜻| TCP |UDP|type에서 미리 정의
<br></p>
<p>return 값|-1| 0
|:---:|:---:|:---:|
뜻| 소켓 생성 실패 |소켓을 가리키는 소켓 디스크립터 
<br></p>
<hr>
<h3>Bind</h3>

<p><code>bind(sockfd, serverIP, serverIP length)</code>
: serverIP, serverPort를 socket에 할당</p>
<p>옵션|sockfd | serverIP | serverIP length 
|:---:|:---:|:---:|:---:| 
뜻| fd 파일 디스크립터의 약자,<br> socket의 반환값 |server의 IP Address|sizeof(serverIP) ,<br> 진짜 길이
<br></p>
<p>return 값|-1| 0
|:---:|:---:|:---:|
뜻| 실패 | 성공
<br></p>
<hr>
<h3>Listen</h3>

<p><code>listen(sockfd, backlog)</code>
: 대기 상태</p>
<p>옵션|sockfd |backlog 
|:---:|:---:|:---:|
뜻| socket의 반환값 |연결 대기열의 크기,<br> request connection socket이 대기하는 대기열
<br></p>
<p>return 값|-1| 0
|:---:|:---:|:---:|
뜻| 실패 | 성공
<br></p>
<hr>
<h3>Accept</h3>

<p><code>accept(sockfg, clientIP, clientIP_length)</code>
: connection 수락, connectionSocket 생성
→ bind와 형식이 거의 같지만 server가 client로 전송하는 것.</p>
<hr>
<h3>Read, Write</h3>

<p>: 데이터 전송 과정</p>
<p><code>read</code>
<code>write(connectSocket, message, sizeof(message))</code></p>
<p>옵션|connectSocket|message|sizeof(message)
|:---:|:---:|:---:|:---:|
뜻| 연결에 사용한 connectionSocket | client에게 보낼 메시지 | 메시지의 크기
<br></p>
<hr>
<h3>Close</h3>

<p>: connection 끊기</p>
<p><code>close(connectSocket)</code></p>
<hr>
<h3>전체적인 흐름</h3>

<p><img src="https://velog.velcdn.com/images/_hye/post/5d194e87-ffd1-4bea-9758-9e440079a1aa/image.png" alt=""> : python 기준으로 <code>send()/recv()</code>는 <code>read()/write()</code>와 같은 용도이다.</p>
<hr>
<h3>Socket vs RestAPI</h3>

<p>→ <span style="color:red">RestAPI는 실시간 통신이 아니다.</span></p>
<p><code>RestAPI</code> : server와 client가 <strong>메시지를 주고받을 때마다</strong> 연결하고 종료하는 형식</p>
<p><code>socket</code> : 한번 연결을 하면 <strong>메시지를 다 주고받기 전까지 종료하지 않고</strong>
메시지를 다 주고받은 후에야 close를 통해 연결을 종료한다.</p>
<p>통신방법|RestAPI|Socket
|:---:|:---:|:---:|
연결| data를 한번 주고받을 때마다 | data를 보내기 위한 connection을 맺을 때
종료 | data를 한번 주고받을 때마다|client와 server가 data를 모두 다 주고받았을 때만</p>
<p><a href="https://colinch4.github.io/2021-05-14/Rest_%EC%86%8C%EC%BC%93_%EC%B0%A8%EC%9D%B4/">https://colinch4.github.io/2021-05-14/Rest_%EC%86%8C%EC%BC%93_%EC%B0%A8%EC%9D%B4/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Socket) 개념정리]]></title>
            <link>https://velog.io/@_hye/Socket-%EA%B0%9C%EB%85%90%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@_hye/Socket-%EA%B0%9C%EB%85%90%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 30 Jan 2024 08:09:21 GMT</pubDate>
            <description><![CDATA[<h3>통신 5계층</h3>

<p><img src="https://velog.velcdn.com/images/_hye/post/2d829a67-57e9-4c9a-b6e6-dfe9bcb550f8/image.png" alt="">
→ application계층은 app에 의해 관리되며 나머지계층은 os(운영체제)에 의해 관리된다.</p>
<hr>
<h3>Socket</h3>
: 메시지를 내보내고 받는 <span style="color:red">통로</span> → 메시지가 아니라 통로임을 주의하기

<p>= 프로그램이 네트워크에서 데이터를 주고받을 수 있도록 네트워크 환경에 연결할 수 있게 만들어진 <strong>연결부</strong>로 application과 transport계층 사이에 생성된다.
<img src="https://velog.velcdn.com/images/_hye/post/f49aa1de-2317-4260-bc65-73f28a1a93ec/image.png" alt=""></p>
<p>(1) TCP와 UDP의 차이점</p>
<ul>
<li>TCP : <span style="color:slateblue">신뢰할 수 있는 </span> 통신서비스 (loss, delay)가 거의 없음 
→ 손실이 있어서는 안되는 email, web</li>
<li>UDP : <span style="color:indianred">신뢰할 수 없는 </span> 통신 서비스, 손실에 대한 복구가 없음 
→ 손실보다는 속도가 더 중요한 Youtube</li>
<li>SSL : 암호화된 TCP Conntection을 제공함.</li>
</ul>
<p>(2) HTTP
: application 계층에서 browser(HTTP Client)와 Web Server(HTPP Server) 사이에 교환하는 <strong>메시지</strong>
→ 서버는 과거 요청을 기억하지 않는다.</p>
<ul>
<li><p>Non-persistent-HTTP
: client와 server 사이에 <span style="color:red">메시지를 보낼 때마다 TCP Connection을 맺어야함</span>
<img src="https://velog.velcdn.com/images/_hye/post/f8bdcee0-a45d-46d6-84d4-99897273eb68/image.png" alt=""></p>
</li>
<li><p>Persistent HTTP 
: client와 server사이에 <span style="color:red">하나의 TCP Connection</span>으로 여러 메시지를 보낼 수 있음</p>
</li>
</ul>
<hr>
<h3>Socket 통신과정 - UDP</h3>

<p><img src="https://velog.velcdn.com/images/_hye/post/6508b7a1-214b-419f-8086-2c73c5e94505/image.png" width="60%" height="n%">→ server와 client 각자 socket을 생성 
→ client에서 <span style="color:orange">message + serverIP / serverPort를 사용해 메시지를 전송</span>
→ server에서 read, write 후에 serverSocket으로 들어온 메시지안에서 clientIP와 clientPort를 사용해 reply
→ <span style="color:green">client에서 read하고 socket을 close함.</span></p>
<ul>
<li>UDP_Client.py
<img src="https://velog.velcdn.com/images/_hye/post/d46dfaeb-13cb-4a49-b2a0-990c8407834d/image.png" alt="">→ Socket 생성(socket) 
→ 메시지 전송(sendto) : 암호화된 message + 보낼 serverIP와 serverPort
→ 메시지 받기(recvfrom) : 서버에서 보낸 modifiedMessage와 serverAddress
→ Socket 닫기(close)</li>
</ul>
<ul>
<li>UDP_SERVER.py
<img src="https://velog.velcdn.com/images/_hye/post/6b8b84da-24a6-4527-86a8-00e495fccac8/image.png" alt="">→ Socket 생성 (socket)
→ Socket에 서버 주소 할당 (bind) : serverIP, serverPort
→ 메시지 받기 (recvform)
→ 메시지 전송 (sendto) : client에 보낼 메시지 + clientAddress</li>
</ul>
<hr>
<h3>Socket 통신과정 - TCP</h3>

<img src="https://velog.velcdn.com/images/_hye/post/3173e581-868f-40a0-8575-d05f21ff5fd9/image.png" width="60%" height="n%">
- server와 client 각자 socket(serverSocket, clientSocket)을 생성

<ul>
<li><p><span style="color:orange">connection 생성</span>
→ server는 request connection이 올때까지 대기(listen)
→ client가 server에 request connection
→ server가 connection 수락(accept), <strong>connectionSocket을 생성</strong></p>
</li>
<li><p><span style="color:green">실제통신</span>
→ client에서 message를 전송
→ server에서 connectionSocket에 담겨있는 message를 read, write한 후에 connectionSocket으로 전송, <strong>connectinoSocket을 close</strong>
→ client에서 read하고 socket을 close함</p>
</li>
<li><p>TCP Client
<img src="https://velog.velcdn.com/images/_hye/post/efc08cb0-54c9-4faf-ba20-0776b9746ae3/image.png" alt="">→ Socket 생성 
→ connect 요청(connect) : serverIP + serverPort
→ 메시지 전송(send) : 암호화된 sentence<br><span style="color:red">* serverIP와 serverPort를 포함하지 않음</span>
→ 메시지 받기(recv) : 서버에서 보낸 modifiedSentence
→ Socket 닫기(close)</p>
</li>
</ul>
<ul>
<li>TCP Server
<img src="https://velog.velcdn.com/images/_hye/post/fadf1a73-8dcd-40d4-a049-c28c1b268a3e/image.png" alt="">→ serverSocket 생성 
→ serverSocket에 서버주소 할당(bind) : serverIP, serverPort
→ serverSocket 대기상태(listen) 
→ serverSocket 요청 수락, connectionSocket 생성(accept)
→ 메시지 받기(recv) : client에서 보낸 sentence
→ 메시지 전송(send) 
→ connectionSocket 닫기(close)<br><span style="color:red"> * UDP server에서는 따로 socket을 닫지 않았음. </span></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Nginx] 해외ip차단 GeoIP 라이브러리  ]]></title>
            <link>https://velog.io/@_hye/Nginx-%ED%95%B4%EC%99%B8ip%EC%B0%A8%EB%8B%A8-GeoIP-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</link>
            <guid>https://velog.io/@_hye/Nginx-%ED%95%B4%EC%99%B8ip%EC%B0%A8%EB%8B%A8-GeoIP-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</guid>
            <pubDate>Mon, 29 Jan 2024 02:00:13 GMT</pubDate>
            <description><![CDATA[<h1> Nginx GeoIP 라이브러리 </h1>

<ul>
<li>nginx 공식 사이트 : <a href="https://docs.nginx.com/nginx/admin-guide/dynamic-modules/geoip/">https://docs.nginx.com/nginx/admin-guide/dynamic-modules/geoip/</a></li>
</ul>
<hr>
<h3> 사용이유 </h3>

<p><img src="https://velog.velcdn.com/images/_hye/post/a9596c44-f2e3-420b-a0c8-5d1cebfc87ec/image.png" alt=""></p>
<p>: 졸업프로젝트를 진행하던 중 aws lightsail에 만들어둔 ec2에 php를 통한 해킹시도가 들어왔었다. github에 코드를 올릴 때 ec2의 고정ip도 같이 올려서 해킹시도를 당한 것 같아서 일단 github의 프로젝트들을 비공개 처리하고</p>
<img src="https://velog.velcdn.com/images/_hye/post/ce640531-d585-4a19-af8a-bbd277727266/image.png" width="80%" height="n%">

<p>스냅샷(백업본)을 이용해 새로운 ec2를 만들어 고정 ip를 새로 발급받았다.
이것으로 해결된 줄 알았으나 계속해서 해킹 시도가 들어왔다...</p>
<p>계속 404 not found로 response가 보내지고 있고, 졸프를 위한 개인적 공간이라서 크게 문제될 것은 없었지만 냅두기에는 또 찝찝해서 어떻게는 이를 해결하고자 고민하였다.</p>
<p>그래서 생각한 것이 아래의 방법이다.</p>
<ol>
<li>aws cloudfront의 waf</li>
<li>aws ec2</li>
<li>흥선대원군의 척화비</li>
</ol>
<hr>
<ol>
<li><p>aws cloudfront의 waf
이 방법의 경우에는 내가 현재 사용하고 있는 것이 aws &#39;lightsail&#39;이기에 스냅샷을 기반으로 aws ec2를 생성하거나 새로 만들어야했다.
나 같은 경우에는 이미 aws lightsail에서 진행하고 있었기에 스냅샷을 기반으로 aws ec2를 생성하기로 하였다.
<br>여기까지는 괜찮았는데 aws lightsail의 스냅샷을 cloudfront와 연결하거나 도메인을 구입하는데 추가비용이 또 들고 cloudfront와 waf도 돈이 많이 들어서.. 이 방법은 경제적으로 불가능하다고 생각했다.
<img src="https://velog.velcdn.com/images/_hye/post/d7703017-1372-4f5f-84d5-50ab0ec48ab7/image.png" alt="">
이틀인가 틀어두었을 때 나온 비용이다. 적용도 안시키고 생성만 해두었는데도 많은 돈이 나갔다. 이틀에 5천원..이면 한달이면 7만원 정도가 나간다. lightsail의 한달비용이 6달러인 것을 생각하면 배보다 배꼽이 더 큰 실정이다;</p>
</li>
<li><p>aws ec2
이 방법은 그냥 단순히 환경을 lightsail이 아닌 기본 aws ec2를 생성하는 것이다. 졸업프로젝트를 진행하는 다른 동기한테 해킹에 대해 여쭤봤을 때 그 동기는 그런 일이 전혀 없었고, 본인은 aws ec2의 프리티어를 사용하고 있다고 답했다. 이를 보고 혹시 lightsail이라서 해킹시도가 들어오는 것인가? 싶어서 환경을 aws ec2로 옮겨봐겠다 생각한 것이다.
<br> 사실 이 방법도 확실한 해결책이 아니고 그저 옮기면 해킹시도가 없어지지 않을까? 하는 생각으로 나온 방안이라서 채택하기에는 어려움이 있었다.</p>
</li>
<li><p>흥선대원군의 척화비
이 방법은 방화벽 설정을 바꿔서 허용된 ip가 아니면 접속할 수 없게 하는 방법인데 이 방법이 가장 확실할테지만 이 방법은 .. 알다싶이 좋은 방법이 아니다. </p>
</li>
</ol>
<p>결국에는 aws내에서 해결하는 것이 아닌 web server로 사용하고 있던 nginx에서 해결하는 방법을 찾았다.</p>
<hr>
<h3>설치 방법</h3>

<p>ec2 안에서 
<code>nginx -v</code> 명령어를 통해 버전이 1.18 이상이라면 따로 모듈을 설치해야한다.</p>
<p>(1) nginx 설정 
<code>cd /etc/nginx/nginx.conf</code> : nginx의 설정파일에서 GeoIP라이브러리를 설정한다.
→ http 탭 안에 라이브러리 설정코드를 넣어주면 된다.</p>
<pre><code class="language-shell">http {

geoip_country /usr/share/GeoIP/GeoIP.dat;

map $geoip_country_code $allowed_country {
   default no;
   KR yes;
        }
}</code></pre>
<p>: 이 코드는 전체를 차단하고 한국 서버만 열어주는데 
만약 전체를 열어주고 한국 서버만 차단하고 싶다면 <code>default yes; KR no;</code> 설정을 주면 된다.
<img src="https://velog.velcdn.com/images/_hye/post/b0d2db5a-9304-4468-9bbe-f7ef4d3319c3/image.png" width="80%" height="n%"></p>
<p>(2) 서버 설정
: 각자 사용하고 있는 서버의 설정파일에 코드를 넣어주면 된다.
FastAPI의 경우 <code>/etc/systemd/system/api.service</code>안에 넣어준다.</p>
<pre><code class="language-shell">server{
   location / {
     if ($allowed_country = no) {
       return 403;
     }
  }
}</code></pre>
<p>: 이 코드는 allowed_country, 즉 위에서 설정한 허용된 지역의 서버가 아니라면 403에러를 return하는 설정이다.</p>
<p>후에 <code>sudo restart nginx</code>를 통해 nginx와 서버를 재시작하면 된다.</p>
<p>(3) 확인
해외서버 테스트사이트 <a href="https://www.websitepulse.com/tools/">https://www.websitepulse.com/tools/</a>
이 사이트에서 해외서버에서 진입하는 환경으로 테스트할 수 있다. </p>
<img src="https://velog.velcdn.com/images/_hye/post/ca70dbd4-78a4-40b2-bba0-aad94e055bc5/image.png" width="75%" height="n%">
: 뉴욕에서 진입했을 때 위와 같이 403에러로 접근할 수 없게 된다.



]]></description>
        </item>
    </channel>
</rss>