<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>au_revoir_0.log</title>
        <link>https://velog.io/</link>
        <description>어두운 밤하늘, 밝은 달빛.</description>
        <lastBuildDate>Sat, 11 Mar 2023 19:35:43 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. au_revoir_0.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/summer_luna_0" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[노마드TS] day 5]]></title>
            <link>https://velog.io/@summer_luna_0/%EB%85%B8%EB%A7%88%EB%93%9CTS-day-5</link>
            <guid>https://velog.io/@summer_luna_0/%EB%85%B8%EB%A7%88%EB%93%9CTS-day-5</guid>
            <pubDate>Sat, 11 Mar 2023 19:35:43 GMT</pubDate>
            <description><![CDATA[<h1 id="40-classes"><em>#4.0 Classes</em></h1>
<p>클래스를 사용할 때, ts와js가 어떻게 다른지를 보자.
여기서는 ts로 객체지향 프로그래밍을 해보자. 왜냐면 ts가 클래스 같은 객체지향 기술을 구현하고 많은 기능을 갖고있다고 생각하기 때문이다.
그리고 ts가 많은 양의 반복되는 코드드를 쓰지 않도록 어떻게 막아주는지도 알아보자.</p>
<p>요구: Player라는 함수를 만들고 몇몇의 property들이 있게 해보자. 대게 js에서는 <code>this.firstName = firstName 혹은 this.lastName = lastName</code>으로 쓰여진다.
ts에서는 파라미터들을 써주기만하면 ts가 알아서 constructor함수를 만들어준다.</p>
<pre><code class="language-ts">class Player {
  constructor(
      private firstName:string,
    private lastName:string,
  ){}
}</code></pre>
<p>으로 쓰면 된다.
private는 js로 컴파일되면서 사라진다.</p>
<p>public도 추가해보자.</p>
<pre><code class="language-ts">class Player {
  constructor(
      private firstName:string,
    private lastName:string,
      public nickname:string
  ){}
}

const coco = new Player(&#39;coco&#39;,&#39;kay&#39;,&#39;꼬꼬&#39;); // 정상 동작</code></pre>
<p>마찬가지로 public도 js로 컴파일되면서 사라진다. ts에서만 보호해주는걸로 작동한다.</p>
<p>만약</p>
<pre><code class="language-ts">coco.fistName</code></pre>
<p>으로 코드를 추가했을 때, 오류가 발생한다. 왜냐하면 ts에서 fistName은 private이기 때문이다.
하지만</p>
<pre><code class="language-ts">coco.nickname</code></pre>
<p>은 정상동작한다. public이기 때문이다.</p>
<p>클라스의 가장 멋있는 점은 <code>추상클래스(Abstract Class)</code>이다. </p>
<pre><code class="language-ts">abstract class User {
  constructor(
    private firstName:string,
    private lastName:string,
      public nickname:string
  ){}
}

class Player extends User{

}

const coco = new Player(&#39;coco&#39;,&#39;kay&#39;,&#39;꼬꼬&#39;);</code></pre>
<blockquote>
<p>추상클래스(Abstract Class)는 오직 다른클래스에서 상속받을 수만 있는 클래스이다.
하지만 이 클래스는 직접 새로운 인스턴스를 만들수는 없다.
ex) new User는 추상클래스가 인스턴스를 만들수 없다고 경고 오류를 발생시킨다. 
new Player만 가능하다.</p>
</blockquote>
<p>추상클래스안의 메소드와 abstract method를 보자.
예를 들어 <code>getFullName()</code>의 method를 만들어보자.</p>
<pre><code class="language-ts">abstract class User {
  constructor(
    private firstName:string,
    private lastName:string,
      public nickname:string
  )
  private getFullName(){
    return `${this.fistName} ${this.lastName}`
  }
}

class Player extends User{

}

const coco = new Player(&#39;coco&#39;,&#39;kay&#39;,&#39;꼬꼬&#39;);
nico.getFullName();</code></pre>
<p>private 및 public이 property뿐만 아니라 metho에서도 작동한다.
getFullName이 private이라서 정상적으로 작동하지 않아야함에도 불구하고 js는 정상적으로 작동한다.
하지만 ts에서는 오류를 발생시키며 동작하지 않는다. 
위의 코드 예제는 ts가 에러가 생기기전에 코드를 어떻게 보호해주는지 알려주기위한 예를 보여주기위해 getFullName메소드 앞에 private를 붙인것이다.
private를 제거하면 ts에서도 정상적으로 동작한다.</p>
<p>추상메소드를 만들려면, 메소드를 클래스 안에서 구현하지 않으면 된다.</p>
<pre><code class="language-ts">getFullName(){
    return `${this.fistName} ${this.lastName}`
  }</code></pre>
<p>위의 코드 중 <code>return `${this.fistName}` `${this.lastName}</code> 이 부분이 메소드의 implementation(구현)이다.</p>
<p>하지만 우리는 메소드를 구현해서는 안되고 대신에 메소드의 call signature만 적어둬야한다.
예를 들어, User추상클래스안에 getNickName인 추상메소드가 있다고하자(말이 안되지만 예를 들어보자)</p>
<pre><code class="language-ts">abstract class User {
  constructor(
    private firstName:string,
    private lastName:string,
      private nickname:string // 수저
  )
  abstract getNickName():void; // new
  private getFullName(){
    return `${this.fistName} ${this.lastName}`
  }
}

class Player extends User{
}

const coco = new Player(&#39;coco&#39;,&#39;kay&#39;,&#39;꼬꼬&#39;);
nico.getFullName();</code></pre>
<p>new부분을 보면 return이 void인 메소드의 call signature만 가지고있는걸 볼 수 있다.
<code>abstract getNickName(arr:string):void;</code>처럼 argument를 가질 수 있다.</p>
<p>ts는 Player가 getNickName을 구현해야한다고 알려주고 있다.
Player함수에 마우스를 호버해보면 ts가 Player가 getNickName을 구현하지 않았다고 알려주고 있는걸 확인할 수 있다.</p>
<p>그러면 getNickName을 구현하자.</p>
<pre><code class="language-ts">abstract class User {
  constructor(
    private firstName:string,
    private lastName:string,
      private nickname:string // 수정
  )
  abstract getNickName():void; // new 추상메소드 부분.
  private getFullName(){
    return `${this.fistName} ${this.lastName}`
  }
}

class Player extends User{
  getNickName(){ // new
    console.log(this.nickName) // new
  }
}

const coco = new Player(&#39;coco&#39;,&#39;kay&#39;,&#39;꼬꼬&#39;);
nico.getFullName();</code></pre>
<p>문제는 여기서 <code>console.log(this.nickName)</code>를 사용할 수 없다는 점이다. (this.까지만 입력해도 오류 발생)
왜냐하면 우리가 이것을 private property로 만들었기 때문이다. (private nickname:string).</p>
<p>만약 우리가 property를 private으로 만든다면, 그 클래스를 상속하였을지라도 그 property에 접근할 수 없다. </p>
<p>여기서 보다시피, Player가 User를 상속받지만 Player에서는 this.nickname을 접근할 수 없다.</p>
<p>이러한 것은 필드를 보호하기 위한 방법이 두개(private, public)만 있는게 아니라서 그렇다.
<code>protected</code>라고 또 다른게 하나가 더 있다.</p>
<p><code>protected</code>는 <code>private</code>과 다르다. 만약 private필드가 있다면 private property를 가진 인스턴스는 밖에서 당연히 접근할 수가 없고, 다른 자식클래스에서도 접근할 수 없다.
<code>private</code>는 말 그대로 개인적인것을 말하고, User클래스의 인스턴스나 메소드에서 접근할 수 있으나 이 User 클래스는 추상클래스여서 인스턴스화 할 수 없다.</p>
<p>필드가 외부로부터는 보호되지만 다른 자식 클래스에서 사용되기를 원한다면 <code>private</code>을 쓰지말자.
그럴때는 대신하여 <code>protected</code>를 써라.</p>
<pre><code class="language-ts">abstract class User {
  constructor(
    protected firstName:string, // protected로수정
    protected lastName:string, // protected로수정
      protected nickname:string // protected로수정
  )
  abstract getNickName():void;
  private getFullName(){
    return `${this.fistName} ${this.lastName}`
  }
}

class Player extends User{
  getNickName(){
    console.log(this.nickName) // 여기!, protect로 수정했기때문에 접근 가능.
  }
}

const coco = new Player(&#39;coco&#39;,&#39;kay&#39;,&#39;꼬꼬&#39;);
nico.getFullName();

coco.firstName // new. property를 protected로 수정했기때문에 불가능</code></pre>
<p>이 접근이 불가능하다. 클래스 밖에서는 여기를 접근할 수 없다는 것이다.
하지만 User를 상속하면 user.nickname에 접근할 수 있다.</p>
<p><code>private</code>,<code>public</code>,<code>protected</code>는 접근제어자===보호등급 을 뜻함. 기본적으로 public이다.</p>
<h1 id="41-recap"><em>#4.1 Recap</em></h1>
<p>실습: 단어사전 만들기. 지금까지 배운것을 이용해서 해시맵을 만들어보자. 해시알고리즘을 쓰는 완벽한 해시맵이 될 것이다.</p>
<pre><code class="language-ts">type Words = { // Words 타입이 string만을 property로 가지는 object라는 것을 의미
  [key:string]: string
}

// ex
// let dict :Wors = {
//   &quot;potato&quot;: &quot;food&quot;
// }

class Dict{
  private words: Words
}</code></pre>
<p>위의 코드대로 하면 Dict 클래스함수의 <code>private words:Words</code>에 에러가 뜬다.
왜냐하면 initialzer가 없기 때문이다.
일반적이라면 코드를</p>
<pre><code class="language-ts">class Dict{
  constructor{
    private words: Words
  }{}
}
//js 컴파일
class Dict{
  constructor(words){
    this.words = words;
  }
}</code></pre>
<p>이렇게 <code>constructor</code>안에 쓰기 때문이다. 하지만 js컴파일을 보면 constructor가 words를 지정해는 방식이다. 
우리는 지정해주기를 원하지 않는 방식으로 구현하려한다. 그러니 이런식으로 하면 안된다.</p>
<p>그렇기에 우리는 word를 initializer없이 선언해주고 constructor에서 수동으로 초기화시켜줄 것이다.</p>
<pre><code class="language-ts">type Words = {
  [key:string]: string
}

// 1이것이 내가 만든 사전이다.
class Dict{
  private words: Words; // initializer없이 선언
  constructor(){
    this.words = {}; // constructor에서 수동으로 초기화
  }
  // 4단어를 추가하기 위한 메서드를 만들자.
  add(word:Word){
      if(this.words[word.term] === undefined){
      // 주어진 단어가 아직 사전에 존재하지 않을때
      this.words[word.term] = word.def;
    } 
  }
  // 6term을 이용해서 단어를 불러오는 기능을 만들자.
  def(term:string){
    return this.words[term];
  }
}

// 2각각의 단어 구현을 Word라는 이름을 가진 클래스 함수로 만들자.
class Word{
  constructor(
      public term:string,
    public def:string.
  ){}
}

// 3새로운 Word생성하자.
const kimchi = new Word(&quot;kimchi&quot;,&quot;없으면 밥 못먹어&quot;);

// 5잘 동작하는지 테스트해보자.
const dist = new Dict();
dict.add(kimchi); // 잘 작동

// 7마찬가지로 잘 동작하는 테스트해보자. 이부분에서 kimchui의 definition을 찾을 수 있어야한다.
dict.def(&quot;kimchi&quot;)&quot;</code></pre>
<p>words가 private이므로, dictionary안에서만 words를 보기를 원한다.
하지만 그 전에 term을 이용해서 단어를 불러오는 기능을 먼저 만들자.
<code>add</code>메소드 하단에 <code>def</code>메소드를 추가하자.</p>
<p>여기서 Dict클래스의 constructor안에서 words를 수동으로 초기화했기때문에 Word클래스를 Dict클래스의 add메서드에서 타입처럼 사용할 수 있는것이다. (<code>add(word:Word)</code> 부분).</p>
<p>add메서드의 파라미터부분에 클래스를 입력해준적이 없지만, 클래스를 타입으로 쓸때는 가능한것이다. 보다시피, 여기서 이 파라미터가 이 클래스의 인스턴스이기를 원하면 이렇게 쓸 수 있다.</p>
<p>지금까지 우리는 concrete타입과 generic을 알아봤다. 
클래스를 타입으로 쓴 것은 이번이 처음이다.</p>
<hr>
<p>위 예제를 확장시켜서 추가 삭제등을 추가해보자. === 과제</p>
<p>Dict클래스에서 단어를 삭제하고 단어를 업데이트하는 메소드를 만들자.
Word클래스에서는 단어의 정의를 추가하거나 수정하는 메소드, 그리고 단어를 출력하는 메소드를 만들자.
클래스랑 메소드, 그리고 private나 public같은 것들을 사용해서 뭔가를 만들어봐라.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[노마드TS] day 4]]></title>
            <link>https://velog.io/@summer_luna_0/%EB%85%B8%EB%A7%88%EB%93%9CTS-day-4</link>
            <guid>https://velog.io/@summer_luna_0/%EB%85%B8%EB%A7%88%EB%93%9CTS-day-4</guid>
            <pubDate>Thu, 09 Mar 2023 09:31:48 GMT</pubDate>
            <description><![CDATA[<h1 id="32-polymorphism"><em>#3.2 Polymorphism</em></h1>
<p><code>다형성(polymorphism)</code>
ts에 어떻게 다형성을 주는지 살펴보자. <code>generic</code>이라 불리는 것을.</p>
<blockquote>
<p>다형성이란, 우선 poly는 many,several, much, multi를 뜻을 가지고있고,<br>morphos 혹은 morphism은 form, structure라는 뜻을 가지고 있다.
따라서 다형성이란 <code>여러가지 다른 구조물</code> 이라는 뜻이다.</p>
</blockquote>
<p>함수는 기본적으로 여러가지 다른 모양 또는 다른 형태를 가지고있다. 
앞서 공부했던 내용을 보면 함수는 다른 2~3개의 parameter를 가질 수 있다고 했었다. 또는 ts에서 함수는 string이나 object를 첫번째 파라미터로 가질 수 있다고 했다.</p>
<p>따라서 우리는 이미 약간의 여러가지 모양의 다형성(polymorphism)을 해본것이다.</p>
<p>이번에는 제네릭이 어떻게 좀 더 도움을 줄 수 있는지 알아보자.</p>
<p>타입에 신경쓰지 않고 숫자로 이루어져있는 배열을 받고, 그 배열의 요소인 숫자를 각각 하나씩 print(출력)해주는 함수를 예로 만들어보자.
또한 string으로 이루어져있는 배열을 받고, string을 하나씩 출력하자.</p>
<pre><code class="language-ts">// 첫번째로 call signature를 선언.
type SuperPring = {
  (arr: number[]):void // number배열을 받고, void를 리턴한다. 왜냐면 함수는 아무것도 리턴하지 않으니까.
}

// 그다음에 superPrint함수를 만든다.
const superPrint: SuperPrint = (arr) =&gt; {
  arr.forEach(i=&gt;console.log(i)); // 리턴하지 않고 각 요소를 출력한다.
}</code></pre>
<p>위 예제에서 문제가 있다. 우리는 배열이 number로 이루어져있는지, boolean인지 string인지 정하지 않은 배열을 받아서 출력하려고한다.</p>
<pre><code class="language-ts">// 첫번째로 call signature를 선언.
type SuperPring = {
  (arr: number[]):void
  (arr: boolean[]):void
}

// 그다음에 superPrint함수를 만든다.
const superPrint: SuperPrint = (arr) =&gt; {
  arr.forEach(i=&gt;console.log(i)); 
}

superPrint([1,2,3,]); // 잘 동작
superPrint([true, false, true]); // 잘 동작
superPrint([&#39;a&#39;,&#39;b&#39;,&#39;c&#39;]); // 동작 안 함. 에러 발생.</code></pre>
<p>string배열을 받는 함수를 동작시키기위해서 call signature에 <code>(arr: string[]):void</code>를 추가해야할까?
물론 그렇게해도 동작은 되지만 정답은 아니다.</p>
<p>여기에서 다형성을 사용하여 ts에게 더 나은 방법으로 알려줄 수 있다.</p>
<pre><code class="language-ts">type SuperPring = {
  (arr: number[]):void
  (arr: boolean[]):void
}</code></pre>
<p>위 코드에서 <code>number</code>, <code>boolean</code>은 <code>concreate type</code>이 아니다.
<code>concreate type</code>이란, 우리가 전부터 봐왔던 타입을 말한다.
number타입, boolean타입, string타입, void, unknown이 concreate type이다.</p>
<p>generic이란, 타입의 placeholder같은거다.
generic은 우리가 call signature를 작성할 때, 타입란에 들어올 확실한 타입을 모를 때 사용한다.</p>
<pre><code class="language-ts">type SuperPring = {
  (arr: number[]):void
  (arr: boolean[]):void
  (arr: string[]):void
  (arr: number | boolean[]):void  
}

// 그다음에 superPrint함수를 만든다.
const superPrint: SuperPrint = (arr) =&gt; {
  arr.forEach(i=&gt;console.log(i)); 
}

superPrint([1,2,3,]);
superPrint([true, false, true]);
superPrint([&#39;a&#39;,&#39;b&#39;,&#39;c&#39;]);
superPrint([1,2,true,false]); // 를 받는 함수를 위해 call signature를 수정.</code></pre>
<p>만약 위의 예시처럼 number와 boolean을 갖는 배열을 함수로 보낼때 call signature에 <code>(arr: number | boolean[]):void</code>를 추가해야 할까?
추가하면 물론 동작은 된다. 하지만 배열에 string이 추가되면? 그럼 또 call signature에 또 추가를 해야하고 무수한 가능성에 대해 생각해서 미리 작성해놔야한다.</p>
<p>이것은 옳은 방법이 아니다.</p>
<p>그래서 generic을 사용하는 것이다.
generic을 사용하는 방법은, 먼저 ts에 generic을 사용하고 싶다고 알려야한다.</p>
<pre><code class="language-ts">type SuperPring = {
  &lt;TypePlaceholder&gt;(arr: TypePlaceholder[]):void
 }</code></pre>
<p>위의 코드예시처럼 꺽쇠안에 generic을 입력 후 타입란에도 동일한 generic을 사용하면 된다.
대게 <code>&lt;TypePlaceholder&gt;</code> 라는 이름 대신 <code>&lt;T, V&gt;</code>를 많이 보게 될 것이다. 이름은 상관없다. 이름이 <code>&lt;Potato&gt;</code>가 될 수도 있는 것이다.</p>
<pre><code class="language-ts">type SuperPring = {
   &lt;TypePlaceholder&gt;(arr: TypePlaceholder[]):void
}

// 그다음에 superPrint함수를 만든다.
const superPrint: SuperPrint = (arr) =&gt; {
  arr.forEach(i=&gt;console.log(i)); 
}

superPrint([1,2,3,]); // 잘 동작
superPrint([true, false, true]); // 잘 동작
superPrint([&#39;a&#39;,&#39;b&#39;,&#39;c&#39;]); // 잘 동작
superPrint([1,2,true,false]); // 잘 동작</code></pre>
<p>그러면 아무 문제 없이 잘 동작하는걸 볼 수 있다.
ts가 타입추론을 하게 하는 것이다.
제네릭은 여전히 내가 함수에 타입을 입력하는 것을 허용한다.</p>
<p>다른 예를 들어보자.
만약 superPrint함수의 리턴 타입을 바꾸고 싶다면 함수는 배열을 받게되고 그 배열의 첫번째요소를 리턴하게 만들자.=== superPrint함수는 arr을 받고, 그 배열의 첫 번째 요소를 리턴하자.</p>
<pre><code class="language-ts">type SuperPring = {
   &lt;TypePlaceholder&gt;(arr: TypePlaceholder[]) =&gt; TypePlaceholder
}

// 그다음에 superPrint함수를 만든다.
const superPrint: SuperPrint = (a) =&gt; a[0];

cosnt a = superPrint([1,2,3,]); // 타입 number
cosnt b = superPrint([true, false, true]); // 타입 boolean
cosnt c = superPrint([&#39;a&#39;,&#39;b&#39;,&#39;c&#39;]); // 타입 string
cosnt d = superPrint([1,2,true,false,&#39;a&#39;]); // 타입 string | number | boolean</code></pre>
<p>리턴타입이 ts가 타입추론하여 나온다.</p>
<p>위의 call signature는 <code>type SuperPrint = &lt;T&gt;(arr: T[]) =&gt; T</code>로 자주 쓰인다.</p>
<hr>
<h1 id="33-generics-recap"><em>#3.3 Generics Recap</em></h1>
<p>제네릭은 내가 요구한대로 call signatuer를 생성해줄 수 ㅣ있는 도구다.</p>
<p>superPrint타입 제네릭을 하나 더 추가하고싶다고 예를 들어보자.
아주 쉽다.</p>
<pre><code class="language-ts">type SuperPrint = &lt;T, M&gt;(arr: T[], b:M) =&gt; T

const superPrint: SuperPrint = (a) =&gt; a[0];

cosnt a = superPrint([1,2,3], &#39;x&#39;);</code></pre>
<p>여기서 b부분이 ts가 알게되는 곳이다. ts는 제네릭이 처음 사용되는 지점을 기반으로 이 타입이 무엇인지 알게된다. 
(, &#39;x&#39;) 두번째 인자를 넣음으로써 ts는 이제 두번째 arguments가 함수에서 제네릭으로 되었다는것을 알게된다.
<strong>ts는 제네릭을 처음 인식했을 때와 제네릭의 순서를 기반으로 제네릭의 타입을 알게된다.</strong></p>
<hr>
<h1 id="34-conclusions"><em>#3.4 Conclusions</em></h1>
<p>제네릭 마무리
앞으로 우리는 제네릭을 사용해서 직접 call signature를 만들 일은 없을 것이다.
라이브러리를 만드는 개발자는 유용하겠지만, 그 외 대부분의 경우에는 직접 제네릭을 작성할 일은 없을 거다.</p>
<p>다른 사용 방법을 보자. 왜냐하면 함수의 call signature 외에 이것을 또 어디서 쓸 수 있는지 아는것도 중요하기 때문이다.</p>
<p>위에서 들었던 예시는 이런식으로 모양을 변형할 수 있다. 위의 예시와 똑같은 동작을 한다.</p>
<pre><code class="language-ts">function superPrint&lt;T&gt;(a: T[]){
  return a[0]
}

cosnt a = superPrint([1,2,3], &#39;x&#39;);</code></pre>
<p>또는 제네릭을 이런식으로 사용할 수 있다.</p>
<pre><code class="language-ts">type Player&lt;E&gt; ={
  name:string
  extraInfo:E
}

const coco: Player&lt;{favFood:string}&gt; = {
  name:&#39;koko&#39;,
  extraInfo:{
    favFood:&#39;kimchi&#39;
  }
}</code></pre>
<p>위의 예제는 이렇게 할 수도 있다.</p>
<pre><code class="language-ts">type Player&lt;E&gt; ={
  name:string
  extraInfo:E
}

type CocoPlayer = Player&lt;{favFood:string}&gt; // new

const coco: CocoPlayer = { // new
  name:&#39;koko&#39;,
  extraInfo:{
    favFood:&#39;kimchi&#39;
  }
}</code></pre>
<p>이렇게도 가능하다</p>
<pre><code class="language-ts">type Player&lt;E&gt; ={
  name:string
  extraInfo:E
}

type CocoExtra = { // new
  favFood:string
}

type CocoPlayer = Player&lt;CocoExtra&gt; // new

const coco: CocoPlayer = {
  name:&#39;koko&#39;,
  extraInfo:{
    favFood:&#39;kimchi&#39;
  }
}</code></pre>
<p>이런식으로 코드 확장이 가능하다.</p>
<p>다른 플레이어를 추가해보자</p>
<pre><code class="language-ts">type Player&lt;E&gt; ={
  name:string
  extraInfo:E
}

type CocoExtra = { // new
  favFood:string
}

type CocoPlayer = Player&lt;CocoExtra&gt; // new

const coco: CocoPlayer = {
  name:&#39;koko&#39;,
  extraInfo:{
    favFood:&#39;kimchi&#39;
  }
}

const vita: Player&lt;null&gt; = {
  name:&#39;vitamin&#39;,
  extraInfo:null
}</code></pre>
<p>type들까리 일종의 상속을 할 수 있다. 굳이 따지자면 지금은 상속이 아닌 재사용이긴하다.</p>
<p>제네릭은 함수에서만 쓰이는게 아니라 정말 많은 곳에서 쓰인다.
예를 들면, 대부분의 기본적인 ts타입은 제네릭으로 만들어져있다.</p>
<p><img src="https://velog.velcdn.com/images/summer_luna_0/post/2e42ca62-ffb5-4251-b429-77af2df28bd2/image.png" alt="">
위 사진에서 자동완성의 ts표준 라이브러리를 보면 <code>Array에 interface Array&lt;T&gt;</code>라고 되어있는걸 볼 수 있다.</p>
<pre><code class="language-ts">type A = Array&lt;number&gt;
let a:A = [1,2,3,4]</code></pre>
<p>위 예시 또한 제네릭을 사용하는 다른 방법 중 하나이다.</p>
<p>만약, printAllNumber라는 함수를 만들고 여기에 number로 이루어진 배열을 받는다면 코드는 아래와 같을것이다.</p>
<pre><code class="language-ts">function printAllNumber(arr:number[]){}
// 혹은 이렇게 쓸 수도 있다.
function printAllNumber(arr:Array&lt;number&gt;){}</code></pre>
<p>Reactjs에서 useState는 제네릭을 받는다.</p>
<pre><code class="language-ts">useState&lt;number&gt;()</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[노마드TS] day 3]]></title>
            <link>https://velog.io/@summer_luna_0/%EB%85%B8%EB%A7%88%EB%93%9CTS-day-3</link>
            <guid>https://velog.io/@summer_luna_0/%EB%85%B8%EB%A7%88%EB%93%9CTS-day-3</guid>
            <pubDate>Wed, 08 Mar 2023 07:33:46 GMT</pubDate>
            <description><![CDATA[<h1 id="-30-call-signatures"><em># 3.0 Call Signatures</em></h1>
<p>call signature===function signature</p>
<p><code>다형성(polymorphism)</code>, <code>오버로딩(overloading)</code>, <code>제네릭(generics)</code></p>
<p><code>call signature</code>란 함수 위에 마우스를 올렸을 때 보게되는걸 말한다.
<img src="https://velog.velcdn.com/images/summer_luna_0/post/be1a0a72-98f4-4222-b2bc-6b19be8d05a2/image.png" alt=""></p>
<p>위 사진의 call signature중 <code>&#39;(a:number, b:number)&#39;</code>는 내가 함수를 어떻게 호출해야하는것인지 알려주고, <code>&#39;=&gt; number&#39;</code>는 함수의 반환 타입을 알려준다.</p>
<p>자, 이제 우리만의 call signature를 어떻게 선언할 것인지 알아보자.</p>
<pre><code class="language-ts">type Add = (a:number, b:number) =&gt; number;</code></pre>
<p>위 코드가 바로 함수의 call signature 타입을 만드는 것이다.</p>
<p>사용은</p>
<pre><code class="language-ts">const add:Add = (a,b) =&gt; a+b</code></pre>
<p>이렇게 하면 된다. 그러면 ts한테 타입이 number라고 말해줄 필요가 없다. add라는 변수가 Add타입을 알고있으니 ts가 유추할 수 있게 된다.</p>
<p>이것은 내가 타입을 만들 수 있고, 함수를 구현하기 전에 그 함수가 어떻게 작동되는지 서술해 둘 수 있다는 것이다. 슈퍼 쿨~</p>
<p>call signature는 함수의 인수 및 반환값의 유형을 알려주는 것이고, 따로 함수를 구현하지 않는다. 또한 javascipt로 컴파일되지 않는다.
call signature는 여러 함수에 동일한 call signature를 사용할 수 있다.</p>
<h1 id="-31-overloading"><em># 3.1 Overloading</em></h1>
<p>function overloading === method overloading
<code>Overloading</code></p>
<p>실제로 많은 오버로딩된 함수를 직접 작성하지 않는다.
우리가 개발할때 사용하는 패키지나 라이브러리들은 오버로딩을 엄청 많이 사용한다.</p>
<p>그래서 우리는 오버로딩이 어떻게 생겼는지, 어떻게 동작하는지를 아는 것이 매우 중요하다.</p>
<blockquote>
<p>오버로딩은 함수가 서로 다른 여러개의 call signatures를 가지고 있을 때 발생시킨다.
함수가 다른 인수 유형을 수락해야하는 경우에 사용해야한다.</p>
</blockquote>
<p>바보같은 예시를 보자</p>
<pre><code class="language-ts">type Add = {
  (a: number, b: number) : number
  (a: number, b: string) : number
}

const add: Add = (a, b) =&gt; {
  if(typeof b === &#39;string&#39;) return a;
  returns a+b
}</code></pre>
<p>이 예시는 매우 나쁜 예시이다.</p>
<p>Next.js에서 사용되는, 실제 개발하면서 자주 접한 오버로딩을 보자.</p>
<pre><code class="language-js">Router.push({
  path: &#39;/home&#39;,
  state: 1
})

.push(/&#39;home&#39;)</code></pre>
<p>object로도 보낼 수 있고, string으로 보낼 수 있다.</p>
<p>ts로 구현해보자.</p>
<pre><code class="language-ts">type Config = {
  path: string,
  state: object
}

type Push = {
  (path:string):void, // argument 1개임.
  (config:Config):void // argument 1개임.
}

//push 구현
const push:Push = (config) =&gt; {
  if(typeof config === &#39;string&#39;) {console.log(config);}
  else{console.log(config.path);}
}</code></pre>
<p>위 예시는 아주 좋은 예시이다.
이것은 패키지나 라이브러리를 디자인할 때 많은 사람들이 사용한다.
오버로딩은 이런식으로 사용된다.</p>
<p>이번에는 다른 여러개의 argument를 가지고 있을 때 발생하는 효과에 대해 알아보자.</p>
<pre><code class="language-ts">type Add ={
  (a: number, b: number) : number // argument 2개임.
  (a: number, b: number, c: number) : number // argument 3개임.
}

const add:Add = (a,b,c) =&gt; {
  return a+b
}</code></pre>
<p>일 때를 예로 들어보자.</p>
<p>add함수에서 argument에 오류가 뜬다.</p>
<p>우리는 지금까지 argument가 한 개이고 개수의 차이가 없는 상황에서의 오버로딩을 보았다.
하지만 지금은 argument의 개수가 차이가 난다.</p>
<p><strong>다른 개수의 파라미터(===argument)를 가지게 되면, 나머지 파라미터도 타입을 지정해줘야한다.</strong>
여기서 c파라미터는 옵션같은거다.</p>
<p>이 call signatur는 Add를 부를 때, a와b를 부를수도있고, 또는 a,b,c를 부를수도 있다는걸 말해주고있다. 그래서 c가 옵션인걸 알수 있다.</p>
<p>이럴때에는 </p>
<pre><code class="language-ts">const add:Add = (a,b,c?:number) =&gt; {
  if(c) return a+b+c
  return a+b
}

add(1,2)
add(1,2,3)</code></pre>
<p>이렇게 파라미터중 c에게 너는 아마도 number일 것이다 라는것을 알려주면 된다.
잘 동작한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[노마드TS] day 2]]></title>
            <link>https://velog.io/@summer_luna_0/%EB%85%B8%EB%A7%88%EB%93%9CTS-day-2</link>
            <guid>https://velog.io/@summer_luna_0/%EB%85%B8%EB%A7%88%EB%93%9CTS-day-2</guid>
            <pubDate>Tue, 07 Mar 2023 09:02:41 GMT</pubDate>
            <description><![CDATA[<h1 id="22-types-of-ts-part-one">#2.2 Types of TS part One</h1>
<p>ts 타입에 대해 알아보자.</p>
<p>예를 먼저 들자.</p>
<pre><code class="language-ts">// 오류 발생 코드
const player:{
  name:string,
  age:number
}={
  name:&#39;coco&#39;
};</code></pre>
<p>일 때, ts에서는 오류를 내뱉는다. 이유는, <code>name</code>과 <code>age</code>를 받는다고 했는데, 실제로는 <code>name</code>하나만 있기 때문이다.
<code>age</code>가 꼭 필수가 아닌 경우에는 어떻게 하냐면</p>
<pre><code class="language-ts">//...
age?:number</code></pre>
<p>라고 수정하면 ts오류가 뜨지 않는다.
이것이 <code>optional parmeter(선택적 변수)</code>를 지정하는 방법이다.</p>
<pre><code class="language-ts">// 수정된 코드
const player:{
  name:string,
  age?:number
}={
  name:&#39;coco&#39;
};</code></pre>
<p>이제 if문으로 age가 10이상일 때 실행하게 될 코드를 적어보자.</p>
<pre><code class="language-ts">// 오류 발생 코드
if(player.age &lt; 10){}</code></pre>
<p>위 코드에서 ts오류가 나타난다. 왜냐하면 <code>age</code>가 필수 입력값이 아니기때문에 <code>age</code>의 타입이 <code>number</code>또는 <code>undefined</code>가 들어올 수 있기 때문이다. </p>
<p>그래서 <code>player.age</code>가 있고, 있다면 실행하게끔 수정을 해야한다. <code>&amp;&amp;</code>연산자를 사용한다.</p>
<pre><code class="language-ts">// 수정된 코드
if(player.age &amp;&amp; player.age &lt; 10){}</code></pre>
<hr>
<p>자, 그렇다면 무수히 많은 player가 있다고 가정해보자.
그러면 코드를 이렇게 작성하게 될 것이다.</p>
<pre><code class="language-ts">const playerJin:{
  name:string,
  age?:number
}={
  name:&#39;jinjin&#39;
};

const playerSol:{
    name:string,
      age?:number
}={
  name:&#39;solsol&#39;,
  age: 2
};

.
.  
.</code></pre>
<p>위의 코드를 보면 어떠한가? 중복된 타입 지정이 거슬리지 않는가??
그렇다. 위의 코드는 중복된 코드를 <code>Alias(별칭)타입</code>을 생성해서 따로 뺄 수 있다.</p>
<p>코드를 수정해보자</p>
<pre><code class="language-ts">type Player = {
  name:string,
  age?:number
}

const playerJin:Player={
  name:&#39;jinjin&#39;
};

const playerSol:Player={
  name:&#39;solsol&#39;,
  age: 2
};</code></pre>
<p>코드가 한 결 간결하지고 깔끔해졌다.
타입별칭을 사용할 때는 항상 <code>type</code>으로 선언을 하고 선언명의 첫문자는 꼭 대문자여야 한다.</p>
<p>여기서 더 나아가면, Player라는 별칭을 타입으로 사용하여<code>jin</code>과 <code>sol</code>모두 player라는걸 알 수 있기때문에 <code>playerJin</code>을 <code>jin</code>으로, <code>playerSol</code>을 <code>sol</code>로 변수이름을 수정할 수 있다.</p>
<pre><code class="language-ts">type Player = {
  name:string,
  age?:number
}

const jin:Player={
  name:&#39;jinjin&#39;
};

const sol:Player={
  name:&#39;solsol&#39;,
  age: 2
};</code></pre>
<p>여기서 Alias는 object만 가능한것이 아닌 여러 타입이 가능하다.</p>
<pre><code class="language-ts">type Age = number;
type Name = string;
type Player = {
  name: Name,
  age: Age
}</code></pre>
<p>하지만 위 처럼 과도하게 재사용하는 것은 좋지 않은 방법이다.
<strong>코드가 깔끔하고 명확해질 때까지만 이 작업을 하면 된다.</strong></p>
<hr>
<p>이번엔 함수의 return값의 타입을 지정하는 방법을 배워보자.
조건: 이 함수는 player의 object를 만들고 그 결과로 player를 반환한다.</p>
<pre><code class="language-ts">type Age = number;
type Name = string;
type Player = {
  name: Name,
  age: Age
}

function playerMaker(name:string){
  return{
    name // name:name 과 동일하다. key value가 똑같은 이름이면 하나만 써도 됨.
  }
}</code></pre>
<p>위의 코드에서 ts는 우리가 object를 return하는 것만 알고있다. 타입이string인 name이 들어있는 object를 말이다.
하지만 우리는 ts에게 playerMaker는 Player타입을 return하고 있다고 알려주고싶다.</p>
<pre><code class="language-ts">const coco = playerMaker(&#39;kko&#39;); // 인자에 아무것도 들어있지 않으면(===playerMaker()) 오류를 발생시킴.
coco.age = 2;</code></pre>
<p>위의 코드에서 playerMaker에는 name만 있는 object를 반환하기 때문에 <code>coco.age</code>를 찾을 수 없어서 오류를 일으킨다.
하지만 우리는 위에서 말했듯이 Player타입을 return하고 있다고 알려주고싶다.</p>
<p>방법은 간단하다. 함수 뒤에 타입을 명시하면 된다.</p>
<pre><code class="language-ts">type Age = number;
type Name = string;
type Player = {
  name: Name,
  age: Age
}

function playerMaker(name:string):Player{
  return{
    name // name:name 과 동일하다. key value가 똑같은 이름이면 하나만 써도 됨.
  }
}

const coco = playerMaker(&#39;kko&#39;);
coco.age = 2;</code></pre>
<p>playerMaker함수에서 인자를 받는 괄호 옆에 콜론:을 쓰고 리턴타입을 명시해주면 된다.
그러면 더이상 <code>coco.age</code>에서 오류가 생기지 않는다.</p>
<p>ES6문법인 화살표함수로 바꾸어보자</p>
<pre><code class="language-ts">type Age = number;
type Name = string;
type Player = {
  name: Name,
  age: Age
}

const playerMaker = (name:string) : Player =&gt; ({name});
const coco = playerMaker(&#39;kko&#39;);
coco.age = 2;</code></pre>
<hr>
<h1 id="23-types-of-ts-part-two">#2.3 Types of TS part Two</h1>
<p>js에서는 없는 <code>readonly</code> 동작이 ts에서는 가능하다.
위의 코드를 예로 들어보자</p>
<pre><code class="language-ts">type Age = number;
type Name = string;
type Player = {
  readonly name: Name,
  age: Age
}

const playerMaker = (name:string) : Player =&gt; ({name});
const coco = playerMaker(&#39;kko&#39;);
coco.age = 2;
coco.name = &#39;koko&#39;</code></pre>
<p>Player 타입에서 name속성을 readonly로 바꾸었다. <code>&#39;readonly&#39;</code>를 앞에 추가하면 읽기전용이 되어 수정이 불가하다.
그러므로 <code>coco.name = &#39;koko&#39;</code>로 수정할 때 ts는 오류를 발생시켜준다.</p>
<p>readonly를 다른곳에서도 활용할 수 있다.</p>
<pre><code class="language-ts">const numbersArr : readonly number[] = [1,2,3,4];
numbersArr.push(5);</code></pre>
<p>위 코드처럼 <code>numberArr</code>변수의 타입을 readonly로 정하면 .push할 때 ts에서 &#39;push라는 것이 readonly number[]타입에 존재하지 않기&#39; 때문에 오류를 발생시킨다.
readonly를 지우면 .push가 잘 동작한다.</p>
<p>배열을 바꾸지 않는 .map이나 .filter는 readonly가 붙어도 사용할 수 있다.</p>
<hr>
<p>이번엔 <code>Tuple</code>에 대해 배워보자. tuple도 ts에만 있다.</p>
<blockquote>
<p><code>Tuple</code>은 array를 생성할 수 있게 한다. 최소한의 길이를 가져야하고, 특정 위치에 특정 타입이 있어야한다.</p>
</blockquote>
<p>예를 들어보자</p>
<pre><code class="language-ts">const player : [string, number, boolean] = [&#39;coco&#39;,2,true];</code></pre>
<p>player변수는 ts에게 이 array가 최소 3개의 아이템을 가지며, string,number,boolean순서대로여야 한다는 것을 알릴 수 있다.</p>
<p><strong><code>Tuple</code>은 항상 정해진 개수의 요소를 가져야하는 array를 지정할 수 있다는 것이 중요하다.</strong></p>
<p>튜플로 타입 선언된 변수를 수정할 때 잘못되었다면 바로 알 수 있다.</p>
<pre><code class="language-ts">const player : [string, number, boolean] = [&#39;coco&#39;,2,true];
player[0] = 1; // 오류 발생. 0번째 배열은 항상 string인데 number로 바꿀 수 없다.</code></pre>
<p>readonly와 함께 쓰면 올바른 수정이라 할지라도 수정이 불가능하게 할 수 있다.</p>
<pre><code class="language-ts">const player : readonly [string, number, boolean] = [&#39;coco&#39;,2,true];
player[0] = &#39;koko&#39;;</code></pre>
<hr>
<p><code>any</code> 타입은 타입을 지정하지 않는다면 기본적으로 타입추론에 의해 <code>any</code>가 들어가게 된다.</p>
<pre><code class="language-ts">const aa = []; // 여기서 aa의 타입은 any[]로 되어있다.</code></pre>
<p><code>any</code>는 쉽게말해서 ts에서 벗어나 js가 되고싶을때나 ts에게 바보같은 짓을 하고싶다고 허락받고 싶을 때 사용한다.
<code>any</code>는 ts의 모든 보호장치를 비활성화 시킨다.
무분별한 <code>any</code>는 ts를 활용하지 못하고 있는 것이다. 지양해야한다.</p>
<h1 id="24-types-of-ts-part-three">#2.4 Types of TS part Three</h1>
<p><code>unknown</code>
어떤 타입인지 모르는 변수는 ts에게 어떻게 말해줘야할까? 이럴 때 <code>unknown</code>을 사용하면 된다.</p>
<pre><code class="language-ts">let a: unknown;</code></pre>
<p>a변수의 타입을 <code>unknown</code>으로 해두면, 어떤 작업을 할려면 a변수의 타입을 먼저 확인하는 방식으로 할 수 있다.
예를 들어서 </p>
<pre><code class="language-ts">let a: unknown;

let b = a + 1;</code></pre>
<p>변수 b를 선언하여 a변수에 1을 더한다고 하면 ts는 오류를 발생시킨다.
왜냐하면 a는 unknowm이기 때문에 number인 1을 더할 수 없기 때문이다
이럴때는 위에서 말한것처럼 a변수의 타입을 먼저 확인하는 방식으로 해야한다.</p>
<pre><code class="language-ts">let a: unknown;

if(typeof a === &#39;number&#39;){
  let b = a + 1;
};</code></pre>
<p>이렇게 <code>typeof</code>를 사용하여 a변수의 타입이 number인지 먼저 확인한 후에 맞다면 조건문 안의 코드를 수행하게 된다.</p>
<p>또는 </p>
<pre><code class="language-ts">let a: unknown;

if(typeof a === &#39;number&#39;){
  let b = a + 1;
};
a.toUpperCase();</code></pre>
<p>변수 a를 upperCase(===대문자) 하고싶지만 a는 unknown이기 때문에 못한다.
이럴때에도 a변수 타입을 먼저 확인하는 방식으로 해야한다.</p>
<pre><code class="language-ts">let a: unknown;

if(typeof a === &#39;number&#39;){
  let b = a + 1;
};

if(typeof a === &#39;string&#39;){
  a.toUpperCase();
};</code></pre>
<p>이러면 에러없이 잘 동작한다.</p>
<hr>
<p><code>void</code>
<code>void</code>는 아무것도 return하지 않는 함수를 대상으로 사용한다.</p>
<pre><code class="language-ts">// void발생 함수
function hello(){
  console.log(&#39;hi&#39;);
}</code></pre>
<p>일 때, 함수 hello타입은 void로 되어 있는걸 확인할 수 있다.</p>
<p>void는 따로 지정할 필요가 없다. ts는 함수가 아무것도 return하지 않는다는것을 자동으로 인식한다.</p>
<hr>
<p><code>never</code>
<code>never</code>타입은 많이 사용하진 않지만 무엇인지 아는 것은 중요하다.
<code>never</code>는 함수가 절대 return하지 않을 때 발생한다. </p>
<pre><code class="language-ts">function hello():never{
  return &#39;x&#39;;
}</code></pre>
<p>일 때 return문에 에러가 발생한다.</p>
<p>하지만 return문에 오류를 발생시키면 오류가 나지 않는다.</p>
<pre><code class="language-ts">// 여기서의 hello함수는 return하지 않고 오류를 발생시키는 함수임.
function hello():never{
  throw new Error(&#39;x&#39;);
}</code></pre>
<p>또한 <code>nenve</code>는 타입이 두가지 일수도 있는 상황에 발생할 수 있다.</p>
<pre><code class="language-ts">function hello(name:string|number){
  if(typeof name === &#39;string&#39;){
    name // 여기서의 name 타입은 string이다.
  } else if(typeof name === &#39;number){ 
    name // 여기서의 name 타입은 number이다.
  } else {
    name // 여기서의 name 타입은 never이다.
  }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[노마드TS] day 1]]></title>
            <link>https://velog.io/@summer_luna_0/%EB%85%B8%EB%A7%88%EB%93%9CTS-Why-not-JavaScript</link>
            <guid>https://velog.io/@summer_luna_0/%EB%85%B8%EB%A7%88%EB%93%9CTS-Why-not-JavaScript</guid>
            <pubDate>Mon, 06 Mar 2023 09:26:11 GMT</pubDate>
            <description><![CDATA[<h1 id="15-why-not-javascript"><em>#1.5 Why not JavaScript</em></h1>
<ul>
<li>안정성. 타입 안정성이 큰 장점.</li>
<li>타입 안정성덕분에 런타입에러가 줄고 덕분에 생산성도 높아짐.
ex)</li>
</ul>
<p>만약 js에서 [1,2,3,4,5]인 배열과 boolean인 false를 추가한다면</p>
<pre><code class="language-js">const ex1 [1,2,3,4,5]+false;
console.log(ex1); // 1,2,3,4false</code></pre>
<p>결과는 <code>1,2,3,4false</code>가 된다. 
    - 여기에는 두가지 문제가 있다.
        1. 배열이 사라진다.
        1. boolean이 사라진다.
    - 결국 배열도, boolean이 아닌 string타입으로 변환되어 반환된다.
또다른 예를 들어보자.</p>
<pre><code class="language-js">function addEx(a,b){
  return a/b;
}
addEx(2,3);
addEx(&quot;xxxx&quot;);</code></pre>
<p>위의 함수일 때, <code>addEx(2,3)</code>을 하면 결과는 <code>0.6666666</code>으로 함수의도대로 잘 동작한다.
하지만 <code>addEx(&quot;xxxx&quot;)</code>를 함수에 전달하면 결과는 <code>NaN</code>을 반환한다.</p>
<p>JS는 정말 친절한 언어이다. 개발자가 실수를 할 것이라고 생각하지않고 최대한 반환을 한다.</p>
<p>그래서 <code>addEx</code>함수에 두개의 입력값이 필요한대도 하나의 입력값만 보내도 실행을 시켜 <code>NaN</code>을 반환한 것이다.
에러가 없는것이다. 이것은 결코 개발자에게 좋은 것이 아니다.</p>
<p>다음은 런타임에러를 일으키는 예를 들어보자.</p>
<pre><code class="language-js">const hi = {name:&#39;coco&#39;};
hi.hello();</code></pre>
<p>위의 예로 실행을하면 <code>console창</code>에 에러가 뜬다. 타입에러가 뜬다.
<code>Uncaught TypeError: hi.hello is not a function at &lt;anonymouse&gt;</code>가 뜨는 이 에러는 <strong>유저의 컴퓨터에서 코드가 실행되면 나타나느 에러</strong> 이다.</p>
<p>코드가 실행하기 전에 오류를 띄워주어야한다. 그래서 타입스크립트를 사용하는 개발자가 많아지는 것이다.</p>
<h1 id="20-how-typescript-works"><em>#2.0 How Typescript Works</em></h1>
<ul>
<li>브라우저는 ts를 js로 변환하여 이해한다. Nodejs는 ts, js다 이해한다.</li>
<li>ts는 개발자가 실수하지 않도로 보호해준다.<ul>
<li>Q: 타입스크립트가 어떻게 우릴 보호해주냐</li>
<li>A: 타입스크립트코드가 자바스크립트로 변환되기 전에 발생한다. 즉, ts가 우리의 코드를 확인한 다음에 변환된 js안에서 실수가 일어나지 않게 확인을 해준다.</li>
</ul>
</li>
<li>실수가 있는 ts코드는 런타임에 js코드로 변환되지 않는다. 실수가 없는 ts코드는 js코드로 변환된다.</li>
<li>이러한 보호장치는 런타임(유저가 코드를 실행하는)에 발생하는 것이 아니다.</li>
</ul>
<p>ts에서 같은 js예제를 사용해보자.</p>
<pre><code class="language-ts">const hi = {name:&#39;coco&#39;};
hi.hello();</code></pre>
<p>ts코드에서 저렇게 작성하면 런타임 이전에 코드 작성이 완료된 시점에 <code>hello</code>에 빨간 밑줄이 생길 것이다.
밑줄에 마우스를 갖다대면 <code>Property &#39;hello&#39; does not exist on type &#39;{name:&#39;coco&#39;}&#39;.</code> 라는 에러를 띄어준다.
그렇기에 우리는 실수했다는것과 실행이 안될거라는 것을 알 수 있다.</p>
<p>타입추론 덕에 이러한 보호장치가 가능한 것이다.</p>
<h1 id="21-implicit-types-vs-explicit-types"><em>#2.1 Implicit Types vs Explicit Types</em></h1>
<p> 예</p>
<pre><code class="language-ts">let a = &#39;hello&#39;; // 타입추론을하여 a변수는 string타입을 받는 변수라고 추론한다.
a = &#39;bye&#39;; // string을 할당하였으니 문제가 없다.
a = false; // string타입으로 추론된 a변수에 boolean이 들어오면 에러를 발생?표시?시킨다.
let b : boolean = fasle; // 명시적으로 변수 b는 boolean타입임을 표시했다.
b = &#39;hi&#39;; // boolean타입으로 명시된 변수 b에 string인 hi가 할당되면 에러를 표시한다.

let c = [1,2,3];
let d : number[] = [1,2,3]; 
// 변수 c와 d는 타입이 같다.

// 타입추론과 명시는 어디에서든 가능하다
const player = {
    name:&#39;coco&#39;; // 타입추론으로 string타입인 것을 알 수 있다.
}
player.name = 1; // 타입추론된 string이 아닌 number이기 때문에 에러가 발생.
player.hello(); // player함수에는 hello가없기 때문에 에러 발생.</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[file upload, download]]></title>
            <link>https://velog.io/@summer_luna_0/file-upload-download-%EC%9E%91%EC%84%B1%EC%A4%91</link>
            <guid>https://velog.io/@summer_luna_0/file-upload-download-%EC%9E%91%EC%84%B1%EC%A4%91</guid>
            <pubDate>Wed, 11 Jan 2023 07:10:31 GMT</pubDate>
            <description><![CDATA[<p>파일을 여러개 각각 업로드 해야할 때,</p>
<h1 id="업로드-버튼-한-개">업로드 버튼 한 개</h1>
<pre><code class="language-js">import axios from &#39;axios&#39;;
import React, { useEffect, useRef, useState } from &#39;react&#39;;

const FileLoad = () =&gt; {
  const [file, setFile] = useState();
  const [fileName, setFileName] = useState();
  const [fileArr, setFileArr] = useState([]);
  const [allFile, setAllFile] = useState([]);

  // 파일 업로드
  const handleSubmit = async (e) =&gt; {
    e.preventDefault();

    const formdata = new FormData();
    formdata.append(&#39;키&#39;, 값);
    formdata.append(&#39;키&#39;, 값);
    ...

    try {
      await axios.post(
        &#39;http:// 파일 업로드 api 주소 &#39;,
        formdata,
        { headers: { &#39;Content-Type&#39;: &#39;multipart/form-data&#39; } },
      );
      location.reload(); // 새로고침
    } catch (error) {
      console.log(error);
    }
  };

  // 파일 리스트
  useEffect(() =&gt; {
    const data = { api요구 &#39;키&#39;:값 };
    const fetchData = async () =&gt; {
      try {
        const res = await axios.post(
          &#39;http:// 파일 리스트 api 주소 &#39;,
          data,
          { headers: { &#39;Content-Type&#39;: &#39;application/json&#39; } },
        );

        setAllFile(res.data);
      } catch (error) {
        console.error();
      }
    };
    fetchData();
  }, []);

  const handleClick = (e) =&gt; {
    setFile(e.target.files[0]);
    setFileName(e.target.files[0].name);
  };

  return(
   &lt;&gt;
      &lt;form onSubmit={handleSubmit}&gt;
      &lt;input type=&quot;file&quot; onChange={handleClick} /&gt;
      &lt;button type=&quot;submit&quot;&gt;버튼&lt;/button&gt;
    &lt;/form&gt;
    {allFile.length &amp;&amp;
      allFile.map(file =&gt; {
        return (
          &lt;ul key={file}&gt;
            {file.split(&#39;/&#39;)[1] ? (
              &lt;li&gt;{file.split(&#39;/&#39;)[1]}&lt;/li&gt;
            ) : (
              &lt;li&gt;파일명 : {file.split(&#39;/&#39;)[0]}&lt;/li&gt;
            )}
          &lt;/ul&gt;
        );
      })}
   &lt;/&gt;
  );
};

export default FileLoad;</code></pre>
<h1 id="업로드-버튼-한-개-이상">업로드 버튼 한 개 이상</h1>
<ul>
<li>조건 : form제출 시 파일 세 개를 같이 제출. 
단, 하나의 업로드 버튼에서 세 개의 파일을 업로드하는 것이 아닌, 세 개의 업로드 버튼에서 각각 하나의 파일만 업로드한다.<pre><code class="language-js">// 파일 세 개의 경로(Root)와 이름(Name)을 하나의 state에서 객체로 관리
const [fileRoot, setFileRoot] = useState({
fileOne:&#39;&#39;,
fileTwo:&#39;&#39;,
fileThree:&#39;&#39;
})
const [fileName, setFileName] = useState({
fileOne:&#39;&#39;,
fileTwo:&#39;&#39;,
fileThree:&#39;&#39;
})
</code></pre>
</li>
</ul>
<p>// 파일 업로드
const handleFileUpload = async (e) =&gt; {
    e.preventDefault();</p>
<pre><code>let formdata = new FormData();
 formdata.append(&#39;키&#39;, 값);
 formdata.append(&#39;키&#39;, 값);

try {
  const res = await axios.post(
    &#39;http:// 파일 업로드 api주소 &#39;,
    formdata,
    { headers: { &#39;Content-Type&#39;: &#39;multipart/form-data&#39; } },
  );

  setFileRoot(fileRoot =&gt; ({ // fileRoot나 prevState로 설정해도 결과는 똑같다.
    ...fileRoot,
    [e.target.className]: res.파일의 경로값,
  }));

  setFileName(prevState =&gt; ({ // fileName이나 prevState로 설정해도 결과는 똑같다.
    ...prevState,
    [e.target.className]: res.파일의 이름,
  }));
} catch (error) {
  console.log(error);
}</code></pre><p>  };</p>
<p>// 파일 다운로드
const handleFileDownload = async (e) =&gt; {
    try {
      const data = {&#39;키&#39;:값 };
      const res = await axios.post(
        &#39;http:// 파일 다운로드 api 주소 &#39;,
        data,
        {
          headers: {},
          responseType: &#39;blob&#39;,
        },
      );
&#39;downloadRes&#39;);
      const extractDownloadFilename = res =&gt; {
        const disposition = res.headers[&#39;content-disposition&#39;];
        const fileName = decodeURI(
          disposition
            .match(/filename[^;=\n]<em>=(([&#39;&quot;]).</em>?\2|[^;\n]*)/)[1]
            .replace(/[&#39;&quot;]/g, &#39;&#39;),
        );
        return fileName;
      };
      const blob = new Blob([res.data]);
      const fileObjectUrl = window.URL.createObjectURL(blob);</p>
<pre><code>  const link = document.createElement(&#39;a&#39;);
  link.href = fileObjectUrl;
  link.style.display = &#39;none&#39;;
  link.download = extractDownloadFilename(res);

  document.body.appendChild(link);
  link.click();
  link.remove();

} catch (error) {
  console.log(error);
}</code></pre><p>  };</p>
<p>return(
  &lt;&gt;
      <div> // -----------------------------------파일1업로드,다운로드
        <div>파일 1</div>
        <label htmlFor='file1'>
          &lt;input 
            type=&#39;file&#39;
            name=&#39;file1&#39; 
            className=&#39;fileOne&#39;
            onChange={e=&gt;handleFileUpload(e)} 
          style={{ display: &#39;none&#39; }}
        /&gt;
          <button>
            파일올리기
          </button>
      </label>
        &lt;div 
        onClick={e=&gt;handleFileDownload(e)}
       &gt;
          {fileOne} 업로드 한 파일 내려받기
        </div><br>      </div>
    <div> // -----------------------------------파일2업로드,다운로드
        <div>파일 2</div>
        <label htmlFor='file2'>
          &lt;input 
            type=&#39;file&#39;
            name=&#39;file2&#39; 
          className=&#39;fileTwo&#39;
            onChange={e=&gt;handleFileUpload(e)} 
          style={{ display: &#39;none&#39; }}
        /&gt;
          <button>
            파일올리기
          </button>
      </label>
        &lt;div 
        onClick={e=&gt;handleFileDownload(e)}
       &gt;
        {fileTwo} 업로드 한 파일 내려받기
        </div><br>    </div>
    <div> // -----------------------------------파일3업로드,다운로드
        <div>파일 3</div>
        <label htmlFor='file3'>
          &lt;input 
            type=&#39;file&#39;
            name=&#39;file3&#39;
          className=&#39;fileThree&#39;
            onChange={e=&gt;handleFileUpload(e)} 
          style={{ display: &#39;none&#39; }}
        /&gt;
          <button>
            파일올리기
          </button>
      </label>
        &lt;div 
        onClick={e=&gt;handleFileDownload(e)}
       &gt;
          {fileThree} 업로드 한 파일 내려받기
        </div><br>    </div>
  &lt;/&gt;
);</p>
<p>```</p>
<ul>
<li><p>업로드 설명</p>
</li>
<li><p>각각의 업로드 할 파일버튼을 생성.</p>
</li>
<li><p>하나의 함수<code>handleFileDownload</code>에서 업로드를 처리.</p>
</li>
<li><p><code>onChange</code>시 호출되는 함수에 event를 props로 넘겨준다. 넘겨 받은 함수에서 className을 검색하여, 변수를 선언한 객체의 키값이 className과 같으면 키값과 값을 저장한다.</p>
</li>
<li><p>spread 문법을 사용하였다.</p>
</li>
<li><p>다운로드 설명</p>
</li>
<li><p>파일 다운로드도 업로드와 같다.</p>
</li>
<li><p>다운로드에서 중요한 부분은 <code>blob</code>이다.</p>
</li>
<li><p>파일 이름으로 다운로드하기 위해서는 <code>content-disposition</code>값을 가져와서 디코딩을 필수로 거쳐야한다.</p>
</li>
<li><p>필요한 부분을 정규표현식(regex)과 replace로 추출한다.</p>
</li>
<li><p><code>new Blob()</code> 생성자를 사용하여 유형이 지정된 배열에서 blob을 만들 수 있다.</p>
</li>
<li><p><code>window.URL.createObjectURL()</code>을 사용하여 url을 생성한다.</p>
</li>
<li><p><code>&lt;a&gt;</code>태그를 생성하여 attribute를 설정한다.</p>
</li>
<li><p>설정된 <code>&lt;a&gt;</code>태그를 document.body에 붙인다.</p>
</li>
</ul>
<h4 id="spread-문법이란">Spread 문법(...)이란?</h4>
<ul>
<li>ECMAScript6(2015)에서 새로 추가된 문법이다. 문법 이름처럼 객체 혹은 배열들을 펼칠 수 있게 해준다.</li>
<li>ES6의 Spread 문법(…)을 사용한 배열을 인수로 함수에 전달하면 배열의 요소를 분해하여 순차적으로 파라미터에 할당한다.</li>
<li>반복 가능한 객체 (Iterable Object)를 개별 요소로 분리할 수 있다. </li>
<li>rest문법과 형태는 똑같으나, 사용이 다르다.</li>
</ul>
<hr>
<ul>
<li>Blob
<a href="https://ko.javascript.info/blob">https://ko.javascript.info/blob</a>
<a href="https://www.w3.org/TR/FileAPI/#blob-section">https://www.w3.org/TR/FileAPI/#blob-section</a></li>
<li>content-disposition
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition">https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[내가 만든 쿠키~_~/]]></title>
            <link>https://velog.io/@summer_luna_0/%EB%82%B4%EA%B0%80-%EB%A7%8C%EB%93%A0-%EC%BF%A0%ED%82%A4</link>
            <guid>https://velog.io/@summer_luna_0/%EB%82%B4%EA%B0%80-%EB%A7%8C%EB%93%A0-%EC%BF%A0%ED%82%A4</guid>
            <pubDate>Wed, 04 Jan 2023 07:48:40 GMT</pubDate>
            <description><![CDATA[<p>쿠키에 값 저장, 저장한 값 불러오기.
encode, decode</p>
<p>string.replace MDN 굿~</p>
<hr>
<p>저장할 때</p>
<pre><code class="language-js">import {useCookies} from &#39;react-cookie;
const [cookies,setCookies,removeCookies] = useCookies([&#39;aa&#39;]);
setCookies(&#39;aa&#39;, encodeURIComponent(ㅌㅌㅌ@ㅌㅌㅌ.com));</code></pre>
<p>쿠키에 저장되어있는 aa를 보면 <code>ㅌㅌㅌ%40ㅌㅌㅌ.com</code>으로 값이 저장되어 있다. 
%40 === @</p>
<hr>
<p>불러올때,</p>
<pre><code class="language-js">import {useCookies} from &#39;react-cookie;
const [cookies,setCookies,removeCookies] = useCookies([&#39;aa&#39;]);
decodeURIComponent(cookies.userEmail.replace(/%40/i, &#39;@&#39;));</code></pre>
<p>정규표현식을 굳이 써봤다. <code>replace(&#39;40%&#39;,&#39;@&#39;)</code>해도 됨.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Nextjs) URL parameter 제거]]></title>
            <link>https://velog.io/@summer_luna_0/URL-parameter-%EC%A0%9C%EA%B1%B0</link>
            <guid>https://velog.io/@summer_luna_0/URL-parameter-%EC%A0%9C%EA%B1%B0</guid>
            <pubDate>Wed, 26 Oct 2022 05:41:50 GMT</pubDate>
            <description><![CDATA[<p><strong>검색어</strong></p>
<ul>
<li>next qeury hide</li>
<li>nextjs remove query params</li>
<li>nextjs router hide query params</li>
</ul>
<hr>
<h4 id="1-windowhistoryreplacestate-react">1. window.history.replaceState (react)</h4>
<ul>
<li><a href="https://developer.mozilla.org/ko/docs/Web/API/History/replaceState">https://developer.mozilla.org/ko/docs/Web/API/History/replaceState</a></li>
<li><code>history.replaceState(stateObj, title[, url])</code></li>
<li>url은 optional<h2 id="2-routerreplace">2. router.replace()</h2>
<ul>
<li>url에서 모든 쿼리 매개변수를 제거한다.</li>
<li><code>router.replace(&#39;/??&#39;,undefined,{shallow:true});</code></li>
<li>특정 매개변수만 제거한다.</li>
<li><pre><code>const removeQueryParam = (param) =&gt; {
const { pathname, query } = router;
const params = new URLSearchParams(query);
params.delete(param);
router.replace(
 { pathname, query: params.toString() },
 undefined, 
 { shallow: true }
);
};
</code></pre></li>
</ul>
</li>
</ul>
<p>removeQueryParam(&#39;something&#39;);</p>
<pre><code>- https://stackoverflow.com/questions/65606974/next-js-how-to-remove-query-params

## 3. Link태그 내의 &#39;as&#39; prop 사용 
- Link 의 as는 브라우저 URL 표시줄에 표시 될 경로에 대한 선택적 데코레이터이다.
    - https://nextjs.org/docs/api-reference/next/link</code></pre><Link href={{ pathname: "/about", query: { name: "test" } }} as="/about">
    <a>Link with hidden query params</a>
</Link>
```
   - https://www.reddit.com/r/nextjs/comments/j5dkzg/how_to_visit_url_and_hide_its_params_in_browser/

<h2 id="4-push-대신-url-바꾸기replace-the-url-instead-of-push">4. push 대신 url 바꾸기(Replace the URL instead of push)</h2>
<ul>
<li>Link 컴포넌트의 기본 동작은 새로운 URL을 이력(기록) 스택에 푸시하는 것이다. 다음 예와 같이 replace prop을 사용하여 새 엔트리(항목)를 추가하지 않도록 할 수 있다.</li>
<li>replace - 새 URL을 스택에 추가하는 대신 현재 기록 상태를 바꾼다. 기본값은 false이다.<ul>
<li>예) <code>&lt;Link href=&#39;/about&#39; replace&gt; About us &lt;/Link&gt;</code></li>
</ul>
</li>
<li><a href="https://nextjs.org/docs/api-reference/next/link">https://nextjs.org/docs/api-reference/next/link</a></li>
</ul>
<hr>
<h3 id="etc">etc</h3>
<ul>
<li>페이지를 새로고침하지않고 url을 수정하려면 history.pushState()를 사용한다.</li>
<li>window.onpopstate로 뒤로/앞으로 버튼 탐색을 감지하는 데 사용할 수 있다.</li>
<li>shallow?    <ul>
<li>shallow - getStaticProps, getServerSideProps 또는 getInitialProps를 다시 실행하지 않고 현재 페이지의 경로를 업데이트한다. 기본값은 false이다.</li>
<li>shallow Routing(얕은 라우팅)<ul>
<li><a href="https://nextjs.org/docs/routing/shallow-routing">https://nextjs.org/docs/routing/shallow-routing</a><ul>
<li>상태를 잃지 않고 라우터 개체(<code>userRouter</code> 또는 <code>withRouter</code>)를 통해 업데이트된 <code>pathname</code>과 <code>query</code>를 받게 된다.</li>
<li>얕은 라우팅을 활성화하려면 <code>shallow</code>옵션을 <code>true</code>로 설정한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[nextjs 설치]]></title>
            <link>https://velog.io/@summer_luna_0/nextjs-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@summer_luna_0/nextjs-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Sat, 17 Sep 2022 05:59:07 GMT</pubDate>
            <description><![CDATA[<p>NEXT.JS 공식문서</p>
<ul>
<li><a href="https://nextjs.org/docs/api-reference/create-next-app">https://nextjs.org/docs/api-reference/create-next-app</a></li>
</ul>
<p>MSOffice문서</p>
<ul>
<li><a href="https://learn.microsoft.com/ko-kr/windows/dev-environment/javascript/nextjs-on-wsl">https://learn.microsoft.com/ko-kr/windows/dev-environment/javascript/nextjs-on-wsl</a></li>
</ul>
<hr>
<h1 id="automatic-setup">Automatic Setup</h1>
<ol>
<li><code>$ npx create-next-app my-app --use-npm</code><ul>
<li><code>--use-npm</code>을 붙여서 npm을 사용해서 앱을 부트스트랩하도록 CLI에 명시적으로 지시해준다.</li>
<li>nextjs 프로젝트가 생성되면 자동으로 <strong>page폴더</strong>가 생성되고, 터미널에서 <code>npm run dev</code>명령어를 실행할 수 있다.</li>
<li>에디터로 next프로젝트를 실행된 기본 폴더구조.
<img src="https://velog.velcdn.com/images/summer_luna_0/post/d0e0360c-8049-48d0-a131-1f8e35329d9f/image.png" alt=""></li>
<li><code>npm run dev</code>로 개발환경으로 띄운 화면.
<img src="https://velog.velcdn.com/images/summer_luna_0/post/a03070b5-461c-4024-8d88-d3b4f890b2aa/image.png" alt=""></li>
</ul>
</li>
</ol>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js Mui5 Font적용 with @font-face]]></title>
            <link>https://velog.io/@summer_luna_0/Next.js-Mui5-Font%EC%A0%81%EC%9A%A9-with-font-face</link>
            <guid>https://velog.io/@summer_luna_0/Next.js-Mui5-Font%EC%A0%81%EC%9A%A9-with-font-face</guid>
            <pubDate>Wed, 31 Aug 2022 02:49:45 GMT</pubDate>
            <description><![CDATA[<h1 id="nextjs--mui5-환경에서-font-face로-font적용하기">Nextjs + Mui5 환경에서 @font-face로 Font적용하기</h1>
<h3 id="-예제-출처">* 예제 출처</h3>
<ul>
<li>Nextjs 공식 페이지의 &#39;Learn&#39;의 Next.js &#39;Create your first Next.js app&#39; 을 예제로 가져왔습니다. </li>
<li>예제 코드는 &#39;<a href="https://nextjs.org/learn/foundations/about-nextjs?utm_source=next-site&amp;utm_medium=nav-cta&amp;utm_campaign=next-website&#39;">https://nextjs.org/learn/foundations/about-nextjs?utm_source=next-site&amp;utm_medium=nav-cta&amp;utm_campaign=next-website&#39;</a> 이 링크 클릭하세요.</li>
</ul>
<h3 id="나눔스퀘어와-나눔스퀘어ac-차이점">나눔스퀘어와 나눔스퀘어ac 차이점?</h3>
<p>ac가 붙은 폰트의 경우 일종의 하이브리드 폰트로써 레이아웃은 OTF로 처리되었지만 윤곽선은 TTF로 처리되어 좀 더 자연스러운 느낌을 줄 수 있습니다.</p>
<hr>
<h2 id="1-폰트-적용-전">1. 폰트 적용 전</h2>
<p><img src="https://velog.velcdn.com/images/summer_luna_0/post/f049e38d-1cde-4679-8e77-c4b915cf6275/image.png" alt=""></p>
<ul>
<li><code>font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
  Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;</code> 가 설정 되어 있는걸 확인할 수 있다.</li>
</ul>
<h2 id="2-폰트-적용-하기">2. 폰트 적용 하기</h2>
<h3 id="a-폰트를-담을-폴더-생성">a. 폰트를 담을 폴더 생성.</h3>
<ul>
<li>다운로드 받은 폰트 파일을 담을 폴더를 생성한다.</li>
<li>생성할 폴더의 위치는 <code>root</code>폴더의 <code>public</code>폴더 내부에 해준다.</li>
<li>폴더의 이름은 <code>fonts</code>로 했다.
<img src="https://velog.velcdn.com/images/summer_luna_0/post/001ea75c-1501-4f12-91d9-17dfac0af512/image.png" alt=""></li>
<li>생성한 <code>fonts</code> 폴더에 적용하려고 다운받은 폰트를 넣어준다.</li>
</ul>
<h3 id="b-font-face-적용">b. <code>@font-face</code> 적용.</h3>
<ul>
<li><code>root</code>폴더의 <code>styles</code>폴더에서 <code>global.css</code>파일을 연다.</li>
<li>초기 global.css 파일의 내부 코드는 아래 이미지처럼 되어있을 것이다.
<img src="https://velog.velcdn.com/images/summer_luna_0/post/7c1daa75-bc23-4208-9a44-b5c2cd847e80/image.png" alt=""></li>
<li>Visual Studio Code는 Emmet abbreviations(에멧 단축키)과 snippets(스니펫)이 내장되어있다. </li>
<li>그래서 &#39;@f&#39;만 입력해도 제안 목록에 <code>@font-face</code>를 보여준다.</li>
<li>에멧 단축키를 확장하기 위해 <code>Tab</code>키를 누른다.</li>
<li>그러면 이미지의 빨간 화살표처럼 기본 형식으로 코드를 뱉어준다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/summer_luna_0/post/bea2d1ee-f84b-49bb-9072-a92190383fc9/image.png" alt=""><code>@font-face</code> 의 <code>font-family</code> 속성에는 내가 폰트를 가져와서 사용할 이름이기 때문에 명시적으로 지어야 한다. <code>src</code> 속성의<code>url()</code>에 폰트 경로를 입력한다. 추가로 <code>font-weight</code> 속성을 추가한다.</p>
<ul>
<li><code>@font-face</code>를 설정 한 코드
<img src="https://velog.velcdn.com/images/summer_luna_0/post/21ef198f-a54a-4199-a4a9-647e10479d7b/image.png" alt=""></li>
</ul>
<h3 id="c-font-family-적용">c. <code>font-family</code> 적용.</h3>
<ul>
<li><p>적용하려는 컴포넌트에 css를 적용 한다.</p>
<ul>
<li>CSS 스타일 적용하는 세 가지 방법</li>
<li><strong>인라인 스타일(inline style)</strong></li>
<li>내부 스타일 시트(internal style sheet)</li>
<li>외부 스타일 시트(external style sheet)</li>
</ul>
</li>
<li><p>인라인 스타일로 CSS 적용</p>
<ul>
<li>적용하려는 태그에 인라인 스타일로 css 적용
<img src="https://velog.velcdn.com/images/summer_luna_0/post/1238afb7-1fa6-45de-9908-96d8f25f1231/image.png" alt=""><code>header</code> 태그인 <code>&lt;h1&gt;&lt;/h1&gt;</code> 태그 내부에 <code>style</code>속성을 추가해서 <code>font-family</code> 속성을 입력하고 <code>global.css</code>에서 설정한 <code>@font-face</code>의 <code>font-family</code>를 입력하면 적용이 된다.</li>
</ul>
</li>
<li><h1></h1> 태그에 인라인 스타일 적용한 후

</li>
</ul>
<p><img src="https://velog.velcdn.com/images/summer_luna_0/post/31348f4e-e87a-417c-8834-950160f34eb0/image.png" alt=""><code>global.css</code>에서 설정한 <code>font-family</code>속성이 지워지고 새로 설정한 폰트가 적용되었다.</p>
<h2 id="3-폰트-적용-후">3. 폰트 적용 후</h2>
<p><img src="https://velog.velcdn.com/images/summer_luna_0/post/f7230ad1-0f00-45cb-b0ad-44b2994f01b6/image.png" alt=""></p>
<ul>
<li>&#39;January 2, 2020&#39;인 날짜 컴포넌트에 <code>font-family</code>속성에 &#39;NanumSquareR&#39; 을 적용했고, 본문 컴포넌트에는 &#39;NotoSansKR_Regular&#39; 를 적용했다.</li>
</ul>
<hr>
<h2 id="그-외--nextjs-_documentjs-파일에서-폰트-설정">그 외 : Next.js <code>_document.js</code> 파일에서 폰트 설정</h2>
<pre><code>                        ** 추후 작성 예정**</code></pre><ul>
<li>(<a href="https://nextjs.org/docs/basic-features/font-optimization">https://nextjs.org/docs/basic-features/font-optimization</a>)</li>
<li>(<a href="https://nextjs.org/docs/advanced-features/custom-document">https://nextjs.org/docs/advanced-features/custom-document</a>)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[NVM 설치, NVM 에러(exit status 5:)]]></title>
            <link>https://velog.io/@summer_luna_0/nvm%EC%84%A4%EC%B9%98%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@summer_luna_0/nvm%EC%84%A4%EC%B9%98%EC%84%A4%EC%A0%95</guid>
            <pubDate>Fri, 29 Jul 2022 08:54:12 GMT</pubDate>
            <description><![CDATA[<h1 id="nvmnode-version-manager">NVM(Node Version Manager)</h1>
<ul>
<li>nvm은 node js버전 매니저로, 시스템에 여러 개의 nodeJS를 설치하고 사용할 버전을 쉽게 전환 할 수 있도록 도와주는 shell script로 rvm(Ruby version manager)과 비슷한 역할을 수행한다.</li>
</ul>
<ol>
<li><p><del>Node 버전 확인</del></p>
<ul>
<li><del><code>$ node -v</code></del></li>
<li>버전이 없어야 함(설치되어있는 nodejs가 없어야함).</li>
<li>Nodejs홈페이지에서 설치했다면 <strong>삭제 후</strong> NVM으로 설치하자.</li>
</ul>
</li>
<li><p>NVM 설치</p>
<ul>
<li>NVM의 Github Repository에 방문해서 NVM 설치파일을 다운로드. (<a href="https://github.com/coreybutler/nvm-windows">https://github.com/coreybutler/nvm-windows</a>)</li>
<li>README.md -&gt; Download Now!버튼 클릭. </li>
<li>&#39;nvm-setup.zip(4.14MB)&#39; 파일 클릭.</li>
<li>다운로드 한 zip파일을 열어서 &#39;nvm-setup.exe&#39;파일 열기.</li>
<li>다운로드. Next 계속.
<img src="https://velog.velcdn.com/images/summer_luna_0/post/889bd5e9-b21d-4875-a16e-f844408f66fc/image.png" alt=""></li>
</ul>
</li>
<li><p>설치 후 <strong>관리자권한</strong>으로 PowerShell을 열고 windows-nvm을 사용하여 현재 설치된 노드버전을 나열해야한다(이 시점에서는 없어야함).</p>
<ul>
<li><code>$ nvm ls</code>
<img src="https://velog.velcdn.com/images/summer_luna_0/post/944af62d-2ebc-46cf-a54d-6d4256c56862/image.png" alt=""></li>
</ul>
</li>
<li><p>NVM 사용법, 명령어</p>
<ul>
<li><code>$ nvm</code>만 입력하면 사용법이 표시됨.</li>
</ul>
</li>
<li><p>Node 버전 변경</p>
</li>
</ol>
<ul>
<li>먼저 &#39;nvm list available&#39;을 사용해서 현재 LTS 버전 번호를 확인하고, 안정적인 Node.js 최신 LTS릴리스(권장)를 설치한 다음, &#39;nvm install&lt;버전&gt;&#39;을 사용해서 LTS버전을 설치한다.<ul>
<li><code>$ nvm list available</code>
<img src="https://velog.velcdn.com/images/summer_luna_0/post/def62d56-84d4-4603-abc0-62034fd00311/image.png" alt="">
혹은
<img src="https://velog.velcdn.com/images/summer_luna_0/post/921650cd-5f09-426f-9c8a-af89d85d4804/image.png" alt=""><br/></li>
<li><code>$ nvm install &lt;원하는 버전&gt;</code>
  <code>$ nvm install 16.6.2 64</code>
  <code>$ nvm install 10.16.3</code></li>
<li><code>[arch]</code>는 설치될 Node.js환경을 설정해준다. 32bit 환경이라면 32를 입력하고, 64bit환경이면 64를 입력해주면 된다.<em>(잘 안쓰는듯)</em>
  <code>$ nvm arch 64 // or 32</code>로 입력해줘도 된다.
<img src="https://velog.velcdn.com/images/summer_luna_0/post/6c82e311-2612-4648-b759-07c8f2530eb6/image.png" alt=""></li>
<li><code>$ nvm list</code> 또는 <code>$ nvm ls</code> 설치되어 사용할 수 있는 Node.js버전이 조회된다.
<img src="https://velog.velcdn.com/images/summer_luna_0/post/cc68e1fb-c0fe-43ed-9c77-4dc278a37984/image.png" alt="">
<img src="https://velog.velcdn.com/images/summer_luna_0/post/c6b1f98f-c67b-4273-a7ce-6349c57d4d95/image.png" alt=""></li>
<li><code>nvm install</code> 명령어를 통해 설치 시 <code>wsarecv: An existing connection was forcibly closed by the remote host.</code> 이 오류가 출력될 수 있다. 이는 nvm의 문제가 아니라 네트워크와 관련된 문제로 Node.js DIST(<a href="https://nodejs.org/dist/)%EC%A0%91%EA%B7%BC%ED%95%98%EC%97%AC">https://nodejs.org/dist/)접근하여</a> Node.js를 다운로드할 때 해당 URL이 차단되어 발생하는 현상이다. 이런경우에 Node.js DIST에서 Node.js를 직접 다운받아서 설치하면 된다.</li>
<li>Node.js DIST로 이동해서 원하는 버전으로 들어가면 <code>tar.gz</code>, <code>zip</code>등의 설치파일이 많은데 환경에 맞는 Node.js로 압축 형식으로 된 <strong>Node.js</strong>를 다운받고 이 경로로 이동하자.<code>C:\Users\사용자 이름\AppData\Roaming\nvm</code></li>
<li>해당 경로로 이동하면 <code>nvm list</code>나 <code>nvm ls</code>로 확인했던 버전이 존재한다. 이 경로에 Node.js버전 명으로 해서 압축해제 한다.</li>
<li>압축 해제 후 <code>nvm list</code> 또는 <code>nvm ls</code> 명령어로 사용이 가능한 상태로 목록에 출려될 것이다.<br/></li>
<li><code>$ nvm use &lt;사용할 버전&gt;</code>
  <code>$ nvm use 16.6.2</code></li>
<li>(microsoft) 프로젝트에서 사용할 Node.js버전을 변경하려면 새 프로젝트 디렉터리 <code>mkdir NodeTest</code>를 만들고 <code>cd NodeTest</code> 디렉터리로 들어간 다음, <code>nvm use&lt;버전&gt;</code>을 입력하고 사용하려는 버전 번호로 바꾼다.
<img src="https://velog.velcdn.com/images/summer_luna_0/post/273a112c-08da-4633-89e6-431eeb813844/image.png" alt=""></li>
<li><code>$ node -v</code> 또는 <code>npm --version</code> Node.js버전이 변경이 되어있는지 확인.</li>
<li>이 버전 번호는 현재 Node.js버전과 연결된 npm버전으로 자동 변경된다.
<img src="https://velog.velcdn.com/images/summer_luna_0/post/d6295651-739a-4d70-ae3b-3943470ef1a5/image.png" alt=""></li>
</ul>
</li>
</ul>
<h1 id="nvm-에러---exit-status-5">NVM 에러 - exit status 5:</h1>
<ul>
<li><code>nvm use 버전</code> 명령어를 실행했는데, <code>exit status 5:</code> 라는 에러가 떴다.<h3 id="해결한-방법">해결한 방법</h3>
</li>
<li>PowerShell을 <code>관리자로 실행</code>으로 열고 다시 했더니 node 버전 변경이 잘 되었다.</li>
</ul>
<h1 id="nvm-에러---cusers�����appdataroamingnvm-could-not-be-found-or-does-not-exist-exiting">NVM 에러 - C:\Users\�����\AppData\Roaming\nvm could not be found or does not exist. Exiting.</h1>
<ul>
<li>nvm을 설치하고 <code>nvm list</code>명령어를 실행했는데, 위의 캡쳐본의 문구가 뜨긴 하지만 그 윗줄에 nvm을 설치한 경로와 함께 <code>could not be found or does not exist. Exiting.</code> 문구가 같이 떴다.
윈도우 계정을 한글로 설정해서 그런거라고 한다. <h3 id="해결한-방법1">해결한 방법1</h3>
</li>
<li>관리자로 실행한 CMD창에 <code>nvm root &quot;C:\Users\계정명\AppData\Roaming\NVM&quot;</code> 을 입력한다.<ul>
<li>계정명에는 본인 컴퓨터 계정명 입력하면 됨. 아니면 nvm 설치 경로 긁어오면 됨.</li>
<li>꼭 <strong>큰 따옴표</strong>로 묶어주어야하고, 마지막은 <strong>대문자로 NVM</strong>이라고 적어야 한다.<h3 id="또다른-방법">또다른 방법</h3>
</li>
</ul>
</li>
<li>nvm 설치 경로를 c드라이브 하위로 바로 설정하여 설치.</li>
</ul>
<hr>
<p>참고자료</p>
<ul>
<li>(<a href="https://codepathfinder.com/entry/NVM-Nodejs-%EB%B2%84%EC%A0%84-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0">https://codepathfinder.com/entry/NVM-Nodejs-%EB%B2%84%EC%A0%84-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</a>)</li>
<li>(<a href="https://docs.microsoft.com/ko-kr/windows/dev-environment/javascript/nodejs-on-windows">https://docs.microsoft.com/ko-kr/windows/dev-environment/javascript/nodejs-on-windows</a>)</li>
<li>(<a href="https://kdydesign.github.io/2020/09/16/nvm-for-windows/">https://kdydesign.github.io/2020/09/16/nvm-for-windows/</a>)</li>
<li>(<a href="https://kjchoi.co.kr/20">https://kjchoi.co.kr/20</a>)</li>
<li>(<a href="https://sloth.tistory.com/72">https://sloth.tistory.com/72</a>)</li>
<li>(<a href="https://jinnnkcoding.tistory.com/189">https://jinnnkcoding.tistory.com/189</a>)</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[npm 실행 오류 'npm-cli.js']]></title>
            <link>https://velog.io/@summer_luna_0/npm-%EC%8B%A4%ED%96%89-%EC%98%A4%EB%A5%98-npm-cli.js</link>
            <guid>https://velog.io/@summer_luna_0/npm-%EC%8B%A4%ED%96%89-%EC%98%A4%EB%A5%98-npm-cli.js</guid>
            <pubDate>Thu, 27 Jan 2022 08:40:03 GMT</pubDate>
            <description><![CDATA[<h1 id="에러-발견">에러 발견</h1>
<pre><code class="language-html">// error메시지:
Error: Cannot find module &#39;C:\Program Files\Git\node_modules\npm\bin\npm-cli.js&#39;
     at Funtion.Module._resulveFilename(~~)
     at Funtion.Module._load(~~)
     at Funtion.executeUserEntryPoin[as runMain](~~){
   code: &#39;MODULE_NOT_FOUND&#39;,
   requireStack:[]&#39;
}</code></pre>
<p>난 그냥 <code>npm start</code>를 했을 뿐인데, 
위와 같은 에러 메시지가 터미널에 찍혔다.</p>
<h1 id="에러-해결을-위한-구글링">에러 해결을 위한 구글링</h1>
<p>바로 <strong>구글링</strong> 했다.
내가 찾아본 stackoverFlow글들.</p>
<ul>
<li><a href="https://stackoverflow.com/questions/44363066/error-cannot-find-module-lib-utils-unsupported-js-while-using-ionic">https://stackoverflow.com/questions/44363066/error-cannot-find-module-lib-utils-unsupported-js-while-using-ionic</a></li>
<li><a href="https://stackoverflow.com/questions/24721903/npm-npm-cli-js-not-found-when-running-npm">https://stackoverflow.com/questions/24721903/npm-npm-cli-js-not-found-when-running-npm</a></li>
</ul>
<h1 id="해결을-위해-해-본-순서">해결을 위해 해 본 순서</h1>
<h3 id="aka-삽질-순서">aka 삽질 순서</h3>
<ol>
<li><p>VSC 종료 후 재시작.</p>
</li>
<li><p>npm 실행이 안되는 파일을 VSC로 열고 <code>node_modules</code>폴더 삭제.</p>
</li>
<li><p>터미널 창에서 <code>npm i</code>입력. (하지만 위의 동일한 에러가 뜸.)</p>
</li>
<li><p>스택오버플로우 댓글 싹 다 읽기. </p>
<ul>
<li>그 중에 위의 답변대로 다 했지만 해결이 안되었고, pc를 껐다가 켰더니 <em>npm실행이 되었다는 글을 발견</em>.</li>
</ul>
</li>
<li><p><strong>PC 종료 후 재시작.</strong></p>
</li>
<li><p>VSC열고 <code>npm i</code> 입력. </p>
<ul>
<li>위의 node_modules삭제했기때문에 재설치를 위해 npm i 명령어를 사용했음.</li>
</ul>
</li>
</ol>
<h1 id="해결">해결</h1>
<ul>
<li>PC 종료 후 재시작.</li>
</ul>
<h1 id="결과">결과</h1>
<p>너무 잘 됨. </p>
<h1 id="결론">결론</h1>
<p>뭔가가 안된다? 일단 침착하게 PC를 껐다가 키자. 컴퓨터 껐다가 키는게 최고다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[html/css 라디오버튼, input type=radio]]></title>
            <link>https://velog.io/@summer_luna_0/htmlcss-%EB%9D%BC%EB%94%94%EC%98%A4%EB%B2%84%ED%8A%BC-input-typeradio</link>
            <guid>https://velog.io/@summer_luna_0/htmlcss-%EB%9D%BC%EB%94%94%EC%98%A4%EB%B2%84%ED%8A%BC-input-typeradio</guid>
            <pubDate>Wed, 19 Jan 2022 03:29:16 GMT</pubDate>
            <description><![CDATA[<p>일반적으로 input의 type이 </p>
<ul>
<li><code>checkbox</code>인 경우는 <strong>다중선택</strong>이 가능함을 의미하고</li>
<li><code>radio</code>인 경우는 <strong>단일선택</strong>이 됨을 의미한다.</li>
</ul>
<p><code>radio</code>버튼의 경우 <code>name</code>attribute 같은 이름을 주어 연관성을 주지 않는다면 <code>checkbox</code>와 마찬가지로 여러 선택이 된다. 접근성의 의미가 없어진다.</p>
<h3 id="라디오버튼-단일선택-연관성주기">라디오버튼 단일선택. 연관성주기.</h3>
<p><img src="https://images.velog.io/images/summer_luna_0/post/2d27afd4-65cf-4e95-ba45-759d4faf1b72/image.png" alt=""></p>
<pre><code class="language-html">&lt;!-- HTML --&gt;
&lt;input type=&#39;radio&#39; name=&#39;fruit&#39;/&gt; 사과
&lt;input type=&#39;radio&#39; name=&#39;fruit&#39;/&gt; 바나나
&lt;input type=&#39;radio&#39; name=&#39;fruit&#39;/&gt; 감</code></pre>
<p>이렇게 코드를 짜면 <code>name</code>attribute끼리 연관성이 생겨서 세 개의 라디오버튼 중 하나의 라디오버튼만 체크가 가능하다.</p>
<p>하지만 위의 코드로 작성하게 되면 라디오버튼만 클릭해야 체크가 되고, 텍스트를 클릭했을때는 라디오버튼이 체크되지않는다.</p>
<h3 id="라디오버튼-글자클릭-시-라디오버튼-체크-label태그-사용">라디오버튼 글자클릭 시 라디오버튼 체크. label태그 사용</h3>
<h4 id="label태그-사용법1">label태그 사용법1</h4>
<p><img src="https://images.velog.io/images/summer_luna_0/post/712ea1bf-6ec9-4dba-9b8b-6d6f32a2ec64/image.png" alt=""></p>
<pre><code class="language-html">&lt;!-- HTML --&gt;
&lt;input type=&#39;radio&#39; id=&#39;americano&#39; name=&#39;cafe&#39;/&gt;
&lt;label for=&#39;americano&#39;&gt;아메리카노&lt;/label&gt;
&lt;input type=&#39;radio&#39; id=&#39;latte&#39; name=&#39;cafe&#39;/&gt;
&lt;label for=&#39;latte&#39;&gt;라떼&lt;/label&gt;
&lt;input type=&#39;radio&#39; id=&#39;coldbrew&#39; name=&#39;cafe&#39;/&gt;
&lt;label for=&#39;coldbrew&#39;&gt;콜드브루&lt;/label&gt;</code></pre>
<p>과일과의 연관성을 설명한 이미지에서는 달라진게 없이 보이는데, html코드를 보면 <code>label</code>태그의 <code>for</code>attribute를 <code>input</code>태그의 <code>id</code>attribute와 같은 이름으로 주어서 연결을 해주었다. 글자(텍스트)를 클릭해도 해당 라디오버튼에 체크가 된다.</p>
<h4 id="label태그-사용법2">label태그 사용법2</h4>
<pre><code class="language-html">&lt;!-- HTML --&gt;
&lt;label&gt;
  &lt;input type=&#39;radio&#39; name=&#39;cafe&#39;/&gt;
&lt;/label&gt;</code></pre>
<p>코드는 <code>&lt;label&gt;</code>안에 <code>&lt;input&gt;</code>을 중첩시켜 사용할 수 있다. 이 경우 암묵적으로 연관이 되기 때문에 for 및 id속성이 필요없다.</p>
<h4 id="react에서-사용시-알아두어야-할-사항">React에서 사용시 알아두어야 할 사항</h4>
<p><code>form</code>태그 안에서 사용한다면 <code>label</code>의 <code>for</code>attribute가 아닌 <code>htmlFor</code>attribute를 사용한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[react 정규표현식(숫자, 음수, 양수, 영어만)]]></title>
            <link>https://velog.io/@summer_luna_0/react-%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D%EC%88%AB%EC%9E%90-%EC%9D%8C%EC%88%98-%EC%96%91%EC%88%98-%EC%98%81%EC%96%B4%EB%A7%8C</link>
            <guid>https://velog.io/@summer_luna_0/react-%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D%EC%88%AB%EC%9E%90-%EC%9D%8C%EC%88%98-%EC%96%91%EC%88%98-%EC%98%81%EC%96%B4%EB%A7%8C</guid>
            <pubDate>Sun, 16 Jan 2022 11:15:23 GMT</pubDate>
            <description><![CDATA[<h1 id="정규표현식regular-expression">정규표현식(Regular Expression)</h1>
<h3 id="문자열에서-특정-내용을-찾거나-대체-중요한-부분을-가려-뽑을-때-사용한다">문자열에서 특정 내용을 찾거나 대체, 중요한 부분을 가려 뽑을 때 사용한다.</h3>
<h5 id="주로-이메일-전화번호-등의-입력받는-형식이-유효한지-체크할-때-사용한다">주로 이메일, 전화번호 등의 입력받는 형식이 유효한지 체크할 때 사용한다.</h5>
<h3 id="양수-음수-소수점-숫자만-허용하는-정규표현식-리터럴-표기법">양수, 음수, 소수점 숫자만 허용하는 정규표현식 리터럴 표기법</h3>
<pre><code class="language-html">&lt;!--html 구문--&gt;
&lt;input type=&#39;text&#39; onChange={lookingForNum} /&gt;</code></pre>
<p><code>/^[+-]?\d*(\.?\d*)?$/</code></p>
<pre><code class="language-js">// JS 구문
const[numbers,setNumbers] = useState(&#39;&#39;);

const lookingForNum = e =&gt;{
 const num = /^[+-]?\d*(\.?\d*)?$/;

  if(!num.test(e.target.value))return;

}</code></pre>
<h3 id="영문자만-허용하는-정규표현식-리터럴-표기법">영문자만 허용하는 정규표현식 리터럴 표기법</h3>
<p><code>/[^a-zA-Z]/g</code></p>
<h3 id="정규표현식-확인해-볼-수-있는-사이트">정규표현식 확인해 볼 수 있는 사이트</h3>
<p><a href="https://www.regexpal.com/">https://www.regexpal.com/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[VSCode에서 rejected, non-fast-forward 해결하기.]]></title>
            <link>https://velog.io/@summer_luna_0/VSCode%EC%97%90%EC%84%9C-rejected-non-fast-forward-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@summer_luna_0/VSCode%EC%97%90%EC%84%9C-rejected-non-fast-forward-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 26 Aug 2021 13:34:08 GMT</pubDate>
            <description><![CDATA[<h1 id="rejected-non-fast-forward">rejected, non-fast-forward</h1>
<p>vscode에서 github에 <strong>push</strong>할 때
<img src="https://images.velog.io/images/summer_luna_0/post/1631411e-797b-4562-9f01-17c2fa38e72b/rejected%EC%98%88%EC%8B%9C.jpg" alt=""></p>
<h2 id="원인">원인</h2>
<ol>
<li>다른 사용자 또는 멤버(=통상 User)가 remote origin/main에 수정 commit</li>
<li>commit을 pull하기 전에 내가 같은 브랜치에 수정</li>
<li><strong>pull하지 않은 상태에서 commit.</strong></li>
</ol>
<p>나의 경우엔 3번 원인이였다.</p>
<h2 id="해결하기">해결하기</h2>
<h3 id="1-git-pull-시에-allow-unrelated-histories-옵션-추가">1. git pull 시에 –allow-unrelated-histories 옵션 추가</h3>
<p><code>git pull origin main –-allow-unrelated-histories</code>
git에서는 서로 관련 기록이 없는 두 프로젝트를 병합할때 기본적으로 거부하는데, 이 거부를 허용해주는 명령어다. 
branch에 pull할 권한이 있다면 이 방법 사용할 수 있지만,
pull할 권한이 없다면 다음 방법들 중 하나를 사용하자.</p>
<h3 id="2-github에서-git-repository에서-과거-commit내역-삭제">2. GitHub에서 Git Repository에서 과거 Commit내역 삭제</h3>
<p>과거 커밋내역을 삭제해도 상관없는거라면 과거 커밋내역 다 삭제하고 push하면 된다.</p>
<h3 id="3-git-pull-시에---force-옵션-추가">3. git pull 시에 --force 옵션 추가</h3>
<p><code>git push origin main --force</code> 명령어로 과거 커밋 내역이랑 상관없이 현재 커밋 내용으로 덮어씌운다.</p>
<hr>
<p>나의 경우에는 branch를 master -&gt; main으로 바꾸고 commit, push main할 때 오류가 난 상황이었고,
pull 후에 merge를 했는데도 push할 때 오류 해결이 안되서 3번 <code>--force</code>명령어를 사용해서 해결했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[원격 저장소 branch 삭제]]></title>
            <link>https://velog.io/@summer_luna_0/%EC%9B%90%EA%B2%A9-%EC%A0%80%EC%9E%A5%EC%86%8C-branch-%EC%82%AD%EC%A0%9C</link>
            <guid>https://velog.io/@summer_luna_0/%EC%9B%90%EA%B2%A9-%EC%A0%80%EC%9E%A5%EC%86%8C-branch-%EC%82%AD%EC%A0%9C</guid>
            <pubDate>Fri, 18 Jun 2021 08:30:01 GMT</pubDate>
            <description><![CDATA[<p>전제상황</p>
<ul>
<li>Local branch(로컬 브랜치) : main</li>
<li>Remote branch(원격 브랜치) : main, master, feature01, feature02</li>
</ul>
<hr>
<h2 id="원인">원인:</h2>
<ul>
<li>github초대 받고 난 후, branch생성 후 main에 push. 그리고 branch삭제안함. * 3</li>
<li>어찌저찌후에 clone해 놓은 파일 삭제후 다시 clone 함.. vscode켜보니 main브랜치 하나뿐.</li>
<li>github에는 branch가 여러개. github에 있는 branch 삭제하고싶음.</li>
</ul>
<h2 id="과정">과정:</h2>
<ul>
<li><code>git remote update</code><ul>
<li>원격 브랜치에 접근하기위해 업데이트 먼저 해줘야한다.</li>
</ul>
</li>
<li><code>git branch</code><ul>
<li>위 명령어로 브랜치 확인해보면, 기존에 있는 main브랜치 하나만 존재한다.</li>
</ul>
</li>
<li><code>git branch -r</code><ul>
<li><code>-r</code>옵션을 주면 원격 저장소의 branch리스트를 볼 수 있다.</li>
</ul>
</li>
<li><code>git branch -a</code><ul>
<li><code>-a</code>옵션을 주면 로컬, 원격 모든 저장소의 branch리스트를 볼 수 있다.</li>
</ul>
</li>
<li><code>git push origin --delete (브랜치 이름)</code><ul>
<li>원격 저장소의 branch를 삭제하면서 push하는것. </li>
</ul>
</li>
</ul>
<h2 id="검색하면서-알게-된-명령어">검색하면서 알게 된 명령어</h2>
<ul>
<li><code>git checkout -t (origin가 붙은 브랜치이름)</code><ul>
<li>로컬브랜치에 없지만, 원격브랜치에 있는 브랜치를 갖고 오려면 이 명령어를 사용하면 된다.</li>
</ul>
</li>
</ul>
<h2 id="검색하면서-알게-된-지식">검색하면서 알게 된 지식</h2>
<ul>
<li>로컬에서 <code>git branch -r</code>명령어로 보여지는 remote branch는 실제 remote저장소의 branch가 아니다. remote저장소의 branch를 바라보는 참조내역이라고 보면 된다.</li>
<li>원격저장소의 branch가 삭제되어도 로컬에서 <code>git branch -r</code>로 나오는 list는 변화가 없다. 이때는 <code>git fetch --all --prune</code>나 <code>git remote prune origin</code> 둘 중 하나를 쓰면 동기화가 된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[GitHub초대 collaboration하는법, 초대받았을때.]]></title>
            <link>https://velog.io/@summer_luna_0/GitHub%EC%B4%88%EB%8C%80-collaboration%ED%95%98%EB%8A%94%EB%B2%95-%EC%B4%88%EB%8C%80%EB%B0%9B%EC%95%98%EC%9D%84%EB%95%8C</link>
            <guid>https://velog.io/@summer_luna_0/GitHub%EC%B4%88%EB%8C%80-collaboration%ED%95%98%EB%8A%94%EB%B2%95-%EC%B4%88%EB%8C%80%EB%B0%9B%EC%95%98%EC%9D%84%EB%95%8C</guid>
            <pubDate>Fri, 28 May 2021 08:21:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>github에 만든 레포지토리가 있다는 전제 하에 설명.
organization이랑 collaboration은 둘 다 협업을 위한 기능이다.</p>
</blockquote>
<h1 id="collaboration">Collaboration</h1>
<h2 id="구성원-초대-방법a가-b초대">구성원 초대 방법(A가 B초대)</h2>
<ul>
<li>레포지토리의 &#39;settings&#39; -&gt; &#39;Manage access&#39; -&gt; &#39;invite a collaborator&#39; 클릭 -&gt; 초대할 대상 아이디 입력</li>
<li>초대대상의 이메일로 초대링크가 전송됨.</li>
</ul>
<h2 id="구성원-대상--초대받은-대상b">구성원 대상 = 초대받은 대상(B)</h2>
<ul>
<li>github계정 생성때 사용한 이메일 계정으로 들어가서 초대 메일 &#39;view invitation&#39; 클릭.</li>
<li>github창 열리면 &#39;Accept invitation&#39;클릭. (=수락된거임)</li>
</ul>
<h3 id="구성원b이-다음으로-해야할-일">구성원(B)이 다음으로 해야할 일.</h3>
<ul>
<li>Collaboration은 A의 github의 레포지토리에 A,B모두 관리자권한을 동일하게 갖고 운영(?)하는 것. </li>
<li>fork하지말고, window는 gitbash열어서 <strong>clone</strong>하자. (mac은 terminal사용)</li>
</ul>
<p>내가 한 순서.</p>
<ul>
<li>바탕화면에 새로운 폴더 만들어서 그 안에서 실행함.</li>
</ul>
<ol>
<li><code>$ git clone 초대보낸사람의 깃주소</code></li>
<li><code>$ git remote -v</code> 해서 연결이 잘 되었는지 확인.</li>
<li><code>$ git branch 브랜치생성이름</code> 브랜치 바로 생성하기.
 ex) <code>$ git branch mybrnach</code></li>
<li><code>$ git checkout 브랜치생성이름</code> 생성한 브랜치로 이동하기.
 ex) <code>$ git checkout mybrnach</code><pre><code> `Switched to branch &#39;mybranch&#39;`</code></pre></li>
<li><code>$ git branch</code> 입력하면 main에서 내가 생성한 브랜치로 이동된걸 확인할 수 있음.
 ex) main -&gt; mybranch</li>
<li>vscode열어서 코딩작업하기. remote연결했기때문에 add, commit, push, pull ... 다 가능함.<ul>
<li>합쳐야할 작업물들 옮기고 add, commit, push함.</li>
</ul>
</li>
<li>push하고 github레포지토리 보면, &#39;Pull requests&#39;가 생김. 확인해서 merge해주면 끝!</li>
</ol>
<hr>
<p>merge 하기 위해 Create pull request 클릭하면 두가지 방법이 있음.</p>
<ol>
<li>Create pull request</li>
<li>Crate draft pull request</li>
</ol>
<ul>
<li>&#39;draft&#39; 모드는 이 작업이 현재 진행중이며, 아직 merge할 수 있는 상태가 아니란 것을 나타내고, 코드에 대한 토론을 원할때 사용한다.(merge불가능)</li>
</ul>
<hr>
<p>참고 : <a href="https://dsc-sookmyung.tistory.com/24">https://dsc-sookmyung.tistory.com/24</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[mongodb - localhost 연결 오류]]></title>
            <link>https://velog.io/@summer_luna_0/mongodb-localhost-%EC%97%B0%EA%B2%B0-%EC%98%A4%EB%A5%98</link>
            <guid>https://velog.io/@summer_luna_0/mongodb-localhost-%EC%97%B0%EA%B2%B0-%EC%98%A4%EB%A5%98</guid>
            <pubDate>Tue, 18 May 2021 06:58:06 GMT</pubDate>
            <description><![CDATA[<p>본인 mongo v4.4 사용 중.</p>
<ul>
<li><p>문제
npm start 하고 
<code>MongooseServerSelectionError: connect ECONNREFUSED 127.0.0.1:27017
  at NativeConnection.Connection.openUri</code>
이런 오류 메세지가 뜸.</p>
</li>
<li><p>문제 해결
mongodb가 시작되지 않아서였음.</p>
<ol>
<li>제어판 -&gt; 관리도구 -&gt; 서비스</li>
<li>서비스 새 창이 뜨면 MongoDB Server(MongoDB)를 오른쪽마우스 클릭해서 &#39;시작&#39; 클릭.</li>
</ol>
</li>
<li><p>다시 npm start하면 연결 됨!</p>
</li>
</ul>
<p>npm start명령어는 nodemon설치해서 <code>nodemon --watch src/ -r esm src/index.js</code> 명령어로 연결해둠.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[아묻따 CRA에 eslint와 prettier설치 따라하기.]]></title>
            <link>https://velog.io/@summer_luna_0/%EC%95%84%EB%AC%BB%EB%94%B0-react%EC%97%90-eslint%EC%99%80-prettier%EC%84%A4%EC%B9%98-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@summer_luna_0/%EC%95%84%EB%AC%BB%EB%94%B0-react%EC%97%90-eslint%EC%99%80-prettier%EC%84%A4%EC%B9%98-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 27 Apr 2021 06:56:01 GMT</pubDate>
            <description><![CDATA[<h3 id="전제조건-vscode-설치-됨-nodejs설치-됨">전제조건: VSCode 설치 됨, node.js설치 됨.</h3>
<ul>
<li>cra설치하기.
  <code>$ npx create-react-app my-app</code>
  <code>$ cd my-app</code>
  <code>$ npm start</code>
   -&gt; 설명:
  <code>$ npx create-react-app *폴더명지정*</code>  : 위의 코드에서는 <code>my-app</code>이라는 이름으로 만듦.
  <code>$ cd my-app</code> : 만든 폴더로 경로 이동.
  <code>$ npm start</code> : cra실행하는 명령어. 이 후에 <code>$ code .</code> 를 입력하면 vscode창이 뜬다. </li>
</ul>
<h2 id="1-vscode-확장의-마켓플레이스에서-eslint와-prettier-code-formatter-설치">1. VSCode 확장의 마켓플레이스에서 ESLint와 Prettier-Code formatter 설치.</h2>
<h2 id="2-eslint">2. eslint</h2>
<ul>
<li>eslint-config-airbnb설치.<pre><code>$ npm i eslint-config-airbnb</code></pre>yarn쓰는 분들은 아래 명령어 입력.<pre><code>$ yarn add eslint-config-airbnb</code></pre><h3 id="2-1-packagejson에-dependencies에-eslint-config-airbnb가-생겼는지-확인">2-1. package.json에 &quot;dependencies&quot;에 &quot;eslint-config-airbnb&quot;가 생겼는지 확인.</h3>
<pre><code>&quot;dependencies&quot;: {
  &quot;@testing-library/jest-dom&quot;: &quot;^5.11.4&quot;,
  &quot;@testing-library/react&quot;: &quot;^11.1.0&quot;,
  &quot;@testing-library/user-event&quot;: &quot;^12.1.10&quot;,
  &quot;eslint-config-airbnb&quot;: &quot;^18.2.1&quot;,
  &quot;eslint-plugin-prettier&quot;: &quot;^3.4.0&quot;,
  &quot;react&quot;: &quot;^17.0.2&quot;,
  &quot;react-dom&quot;: &quot;^17.0.2&quot;,
  &quot;react-scripts&quot;: &quot;4.0.3&quot;,
  &quot;web-vitals&quot;: &quot;^1.0.1&quot;
},</code></pre></li>
</ul>
<h3 id="2-2-eslint-설정">2-2. eslint 설정.</h3>
<p>루트폴더에 .eslintrc.json 파일 만들기.</p>
<pre><code>&quot;plugins&quot;: [&quot;import&quot;, &quot;html&quot;],
  &quot;extends&quot;: &quot;react-app&quot;,
  &quot;rules&quot;: {
    &quot;prettier/prettier&quot;: &quot;error&quot;,
    }</code></pre><p>저 위의 코드가 중요하다 생각함.</p>
<h2 id="3-prettier">3. prettier</h2>
<ul>
<li>eslint-plugin-prettier 와 eslint-config-prettier설치<pre><code>$ npm i -D prettier eslint-plugin-prettier eslint-config-prettier</code></pre></li>
</ul>
<h3 id="3-1-루트-폴더에-prettierc-파일-만들기">3-1. 루트 폴더에 .prettierc 파일 만들기.</h3>
<pre><code>{
  &quot;tabWidth&quot;: 2,
  &quot;singleQuote&quot;: true,
  &quot;trailingComma&quot;: &quot;none&quot;,
  &quot;bracketSpacing&quot;: true,
  &quot;semi&quot;: true,
  &quot;useTabs&quot;: false,
  &quot;arrowParens&quot;: &quot;avoid&quot;,
  &quot;endOfLine&quot;: &quot;auto&quot;
}</code></pre><h3 id="3-2-루트경로에-eslintrcjson-파일-수정하기">3-2. 루트경로에 .eslintrc.json 파일 수정하기.</h3>
<pre><code>&quot;plugins&quot;: [&quot;import&quot;, &quot;html&quot;,&quot;prettier&quot;],
  &quot;extends&quot;: [&quot;plugin:prettier/recommended&quot;,&quot;react-app&quot;],
  &quot;rules&quot;: {
    &quot;prettier/prettier&quot;: &quot;error&quot;,
    }</code></pre><h2 id="4-vscode---파일---기본설정---설정---설정-열기json">4. VSCode - 파일 -&gt; 기본설정 -&gt; 설정 -&gt; 설정 열기(JSON)</h2>
<p><img src="https://images.velog.io/images/summer_luna_0/post/5ca01b8f-c047-4c0a-b544-8742648a337e/2022-01-29%20(1)_LI.jpg" alt=""></p>
<p><code>&quot;editor.formatOnSave&quot;: true</code> 로 바꿔주기.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript]]></title>
            <link>https://velog.io/@summer_luna_0/TypeScript</link>
            <guid>https://velog.io/@summer_luna_0/TypeScript</guid>
            <pubDate>Sat, 17 Apr 2021 15:12:26 GMT</pubDate>
            <description><![CDATA[<h2 id="">!!!!</h2>
<ul>
<li><p>타입수준? 값수준? : 공용 어휘다.</p>
<ul>
<li>여기서는 타입 수준 코드를 &#39;타입과 타입 연산을 포함하는 코드&#39;를 의미한다.(유효한 자바스크립트 코드는 아니지만, 유효한 타입스크립트 코드)</li>
<li>값 수준 코드는 그 밖의 모든 것을 가리킨다.(유효한 자바스크립트 코드)</li>
</ul>
</li>
<li><p>타입 어노테이션(type annotation)? : 어떤 변수 또는 값의 타입을 명시하는 방식.</p>
</li>
<li><p>타입 별칭? : let, const, var로 변수를 선언해서 값 대신에 변수를 칭하듯이, 타입 별칭으로 타입을 가리킬 수 있다.</p>
<pre><code>  type Age = number;
  type Person = {
      name: string;
      age: Age;
  }

  // 타입스크립트는 별칭을 추론하지 않기 때문에 반드시 별칭의 타입을 명시적으로 정의해야 한다.
  let age: Age = 55;
  let user: Person = {
      name: &#39;hong&#39;;
      age: age;
  }</code></pre><ul>
<li>타입 별칭은 복잡한 타입을 DRY(don&#39;t repeat yourself=반복하지말라)하지 않도록 해준다. 변수가 어떤 목적으로 사용되었는지 쉽게 이해할 수 있도록 도와준다(사바사).</li>
</ul>
</li>
<li><p>유니온(union,합집합)과 인터섹션(intersection,교집합) : 실전에서는 유니온을 자주 사용한다.</p>
</li>
</ul>
<h2 id="ts함수-타입시그니처호출시그니처">ts함수 타입시그니처(=호출시그니처)</h2>
<p>이 문법은 화살표 함수와 아주 비슷하다.</p>
<pre><code>// 타입스크립트 표현
(a:number, b:number) =&gt; number</code></pre><pre><code>// 타입스크립트 활용 표현
type add = (a:number, b:number) =&gt; number</code></pre><p>함수 호출 시그니처는 값이 아닌 타입 정보만 포함한다.(= 타입 수준 코드 정보만 포함한다.)
또한, 바디를 포함하지 않아 타입스크립트가 타입을 추론할 수 없기때문에 반환 타입을 명시해야 한다.</p>
<p>위의 단축형 호출 시그니처를 더욱 명확하게 표현할 수 있다.</p>
<pre><code>//단축형 호출 시그니처
type add = (a:number, b?:number) =&gt; number

//전체 호출 시그니처
type add = {
    (a:number, b?:number):number
}</code></pre><p>간단한 함수라면 단축형을 주로 사용,
복잡한 함수라면 전체 시그니처를 사용.</p>
<h3 id="함수-시그니처-오버로딩overloading">함수 시그니처 오버로딩(overloading)</h3>
<p>오버로드 된 함수 : 호출 시그니처가 여러 개인 함수.</p>
<p>대부분의 프로그래밍 언어에서 여러 매개변수를 인수로 받아 어떤 타입의 값을 반환하는 함수를 선언한 다음, 이 함수가 요구하는 정확한 매개변수 집합을 건네 함수를 호출하면 항상 똑같은 타입의 반환값을 받게 된다. 타입스크립트는 이런 동적 특징을 오버로드된 함수 선언을 통해 제공하고, 입력 타입에 따라 달라지는 함수의 출력 타입은 정적 타입 시스템으로 각각 제공한다. 고급기능에 속한다!</p>
<pre><code>type Vacation = {
    (from: Date, to: Date, destination: string): Holiday;
}

//type Vacation을 편도 여행과 왕복 여행을 지원하도록 코드를 수정했다.
type Vacation = {
    (from: Date, to: Date, destination: string): Holiday;
    (from: Date, destination: string): Holiday;
}

// vacation의 구현 코드다.
let vacation: Vacation = (from, to, destination) =&gt; { //... };</code></pre><p>위의 코드는 에러를 발생시킨다. 왜일까?
-&gt; 조합된 오버로드 시그니처가 존재하지 않기 때문이다.(자동으로 추론하지 않는다)
직접 선언해서 다시 코드를 바꿔보자.</p>
<pre><code>// 편도 여행과 왕복 여행 지원한 코드다.
type Vacation = {
    (from: Date, to: Date, destination: string): Holiday;
    (from: Date, destination: string): Holiday;
} // - 1

// vacation의 구현 코드다.
let vacation: Vacation = (
    from: Date, 
    toOrDestination: Date | string, 
    destination?: string
) =&gt; { // - 2
    //... 
};</code></pre><ul>
<li>1오버로드 된 함수 시그니처 두 개를 선언함.</li>
<li>2vacation구현의 시그니처는, 두 개의 오버로드 시그니처를 수동으로 결합한 결과와 같다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>