<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sky-innerpeace.log</title>
        <link>https://velog.io/</link>
        <description>우당탕탕 개발일기</description>
        <lastBuildDate>Mon, 19 Jun 2023 22:49:43 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sky-innerpeace.log</title>
            <url>https://velog.velcdn.com/images/sky-innerpeace/profile/59eece1a-d1f3-42da-a451-c099e1e09a68/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sky-innerpeace.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sky-innerpeace" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[스프링] SpringBoot 3.0.x부터는 Java 17을 사용해야 한다]]></title>
            <link>https://velog.io/@sky-innerpeace/SpringError1</link>
            <guid>https://velog.io/@sky-innerpeace/SpringError1</guid>
            <pubDate>Mon, 19 Jun 2023 22:49:43 GMT</pubDate>
            <description><![CDATA[<p>약 1년 만에 스프링을 다시 공부하려고 Intellij에서 스프링부트로 만든 프로젝트를 열자마자 이런 오류가 날 반겼다.</p>
<pre><code>&gt; Could not resolve org.springframework.boot:spring-boot-gradle-plugin:3.0.0.
     Required by:
         project : &gt; org.springframework.boot:org.springframework.boot.gradle.plugin:3.0.0
      &gt; No matching variant of org.springframework.boot:spring-boot-gradle-plugin:3.0.0 was found. The consumer was configured to find a runtime of a library compatible with Java 11, packaged as a jar, and its dependencies declared externally, as well as attribute &#39;org.gradle.plugin.api-version&#39; with value &#39;7.5.1&#39; but:
          - Variant &#39;apiElements&#39; capability org.springframework.boot:spring-boot-gradle-plugin:3.0.0 declares a library, packaged as a jar, and its dependencies declared externally:
              - Incompatible because this component declares an API of a component compatible with Java 17 and the consumer needed a runtime of a component compatible with Java 11
              - Other compatible attribute:
                  - Doesn&#39;t say anything about org.gradle.plugin.api-version (required &#39;7.5.1&#39;)</code></pre><p>이것보다 훨씬 길었는데 &#39;no matching variant ...&#39;로 검색했고 원인은 다음과 같았다.</p>
<h3 id="-원인"># 원인</h3>
<p>스프링 부트가 3.0.x 버전으로 업그레이드되면서 Java 11이 아닌 17을 사용한다고 한다. 실제로 오류 문구 중에 이런 내용이 떡하니 있다.</p>
<p>참고로 업그레이드로 인해 javax 패키지 중 몇개 이름을 javax→jakarta로도 변경해야 한다.</p>
<p><strong>Incompatible because this component declares an API of a component compatible with Java 17</strong> </p>
<h3 id="-해결-방법"># 해결 방법</h3>
<p><strong>Preferences(환경설정) → Build,Execution,Deployment → Build Tools → Gradle → Gradle JVM</strong>을 Java 17로 변경해주면 된다.
<img src="https://velog.velcdn.com/images/sky-innerpeace/post/41eb0ccb-6dc6-4bef-9c89-aaf83cecc386/image.png" alt=""></p>
<p><a href="https://stackoverflow.com/questions/74890323/in-intellij-spring-boot-gradle-plugin-3-0-0-no-matching-variant-found">참고한 stackoverflow 글</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Clean Code] 4장 주석]]></title>
            <link>https://velog.io/@sky-innerpeace/Clean-Code-4%EC%9E%A5-%EC%A3%BC%EC%84%9D</link>
            <guid>https://velog.io/@sky-innerpeace/Clean-Code-4%EC%9E%A5-%EC%A3%BC%EC%84%9D</guid>
            <pubDate>Fri, 07 Oct 2022 18:28:48 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>나쁜 코드에 주석을 달지 마라. 새로 짜라.</p>
<ul>
<li>브라이언 W. 커니핸, P. J. 플라우거</li>
</ul>
</blockquote>
<h3 id="주석은-불순하다">주석은 불순하다</h3>
<p>주석은 대체로 불순하다. 우리는 주로 이해하기 어려운 코드에 이해를 돕기 위해 주석을 다는데, 이때는 주석을 달아야 하는 것이 아니라 코드를 고쳐야 한다.</p>
<p>주석으로 설명하는 것이 그렇게 큰 문제인가?에 대한 질문에 저자는 &#39;주석은 자주 거짓말을 한다&#39;고 대답했다. 흔히 프로그래머들은 코드를 리팩토링하지 주석까지 리팩토링하는 경우는 많지 않다고 한다. 설사 한다고 한들, 모든 코드에 대한 주석을 100%로 유지하는 것은 당연히 쉽지 않다.</p>
<h3 id="주석은-나쁜-코드를-보완하지-못한다">주석은 나쁜 코드를 보완하지 못한다</h3>
<p>지저분한 코드에는 주석을 달기 전에 코드를 정리해야 한다.</p>
<h3 id="코드로-의도를-표현하라">코드로 의도를 표현하라</h3>
<p>조건문 안에서 어떤 조건을 검사할때도 해당 조건도 읽으면 이해할 수 있을만큼 명확하게 작성해야 한다.</p>
<pre><code>// 직원에게 복지 혜택을 받을 자격이 있는지 검사한다.
if((employee.flags &amp; HOURLY_FLAG) &amp;&amp; (employee.age &gt; 65)</code></pre><pre><code>if(employee.isEligibleForFullBenefits())</code></pre><h3 id="좋은-주석">좋은 주석</h3>
<h4 id="법적인-주석">법적인 주석</h4>
<p>저작권 정보나 소유권 정보 등 소스 파일 첫머리에 법적인 이유로 들어가는 특정 주석은 붙여주어야 한다.</p>
<h4 id="정보를-제공하는-주석">정보를 제공하는 주석</h4>
<p>반환값이나 파라미터값을 설명하는 기본 정보 제공 주석은 때때로 도움이 된다.
하지만 이보다는 함수 이름에 정보를 담는 편이 좋다.</p>
<pre><code>// 테스트 중인 Responder 인스턴스를 반환한다.
protected abstract Responder responderInstance();</code></pre><h4 id="의도를-설명하는-주석">의도를 설명하는 주석</h4>
<p>겉으로 쉽게 드러나지 않는 저자의 의도를 주석으로 설명해주는 것도 좋다.</p>
<h4 id="의미를-명료하게-밝히는-주석">의미를 명료하게 밝히는 주석</h4>
<p>반환값이나 인수를 다양한 이유로 커스텀하지 못해 복잡해보일 땐 의미를 명료하게 밝히는 주석을 달면 도움이 된다. 하지만 코드와의 일관성을 해치기 쉬우므로 더 나은 방법을 코드단에서 찾는 것이 중요하다.</p>
<h4 id="결과를-경고하는-주석">결과를 경고하는 주석</h4>
<p>테스트 케이스 위에 &#39;~~때는 실행하지 마십시오&#39;라고 다는 주석이 이에 해당한다.</p>
<h4 id="todo-주석">TODO 주석</h4>
<p>모든 코드가 한 번에 완성되거나 확정되지는 않는다. 이럴 때 TODO 주석을 남겨 어떤 식으로 확장해나가야 하는지 적어두면 도움이 된다.</p>
<h4 id="중요성을-강조하는-주석">중요성을 강조하는 주석</h4>
<h3 id="나쁜-주석">나쁜 주석</h3>
<h4 id="주절거리는-주석">주절거리는 주석</h4>
<p>꼭 필요하다고 판단하지 않은 주석을 그냥 매뉴얼대로 달아버리면 다음에 보는 사람은 이해할 수가 없다.</p>
<h4 id="같은-이야기를-중복하는-주석">같은 이야기를 중복하는 주석</h4>
<p>코드 내용을 그대로 서술하는 주석은 좋지 않다.</p>
<pre><code>// this.closed가 true일 때 반환되는 유틸리티 메서드이다.
// 타임아웃에 도달하면 예외를 던진다.
public synchronized void waitForClose(final long timeoutMillis) throws Exception
{
  if(!closed)
  {
    wait(timeoutMillis);
    if(!closed)
      throw new Exception(&quot;MockResponseSender could not be closed&quot;);
  }
}</code></pre><h4 id="오해할-여지가-있는-주석">오해할 여지가 있는 주석</h4>
<p>실제 코드가 동작하는 것과 살짝 다른 정보로 주석이 달려있을 경우</p>
<h4 id="의무적으로-다른-주석">의무적으로 다른 주석</h4>
<p>모든 함수에 Javadocs를 달거나 모든 변수에 주석을 달아야 한다는 규칙은 코드를 복잡하게 만들고 잘못된 정보를 제공할 여지를 만들게 됨</p>
<pre><code>/*
@param tite CD 제목
@param author CD 저자
@param tracks CD 트랙 숫자
*/

public void addCD(String title, String author, int tracks) {
  CD cd = new CD();
  cd.title = title;
  cd.author = author;
  cd.tracks = tracks;
  cdList.add(cd);
  }</code></pre><h4 id="이력을-기록하는-주석">이력을 기록하는 주석</h4>
<ul>
<li>모듈에 가한 변경을 모두 기록하는 주석</li>
<li>소스 코드 관리 시스템이 없을 땐 많이 사용됨</li>
</ul>
<h4 id="있으나-마나-한-주석">있으나 마나 한 주석</h4>
<p>너무 당연한 내용의 주석은 쓸모가 없다. </p>
<pre><code>/**
* 기본 생성자
*/
protected AnnualDateRule() { ... }</code></pre><pre><code>//월 중 일자
private int dayOfMonth;</code></pre><h4 id="닫는-괄호에-다는-주석">닫는 괄호에 다는 주석</h4>
<p>함수의 의미를 닫는 괄호 뒤에 달아야 한다고 판단된다면 함수를 정리하자.</p>
<h4 id="주석으로-처리한-코드">주석으로 처리한 코드</h4>
<p>주석으로 처리된 코드는 다른 사람들이 지우기를 주저한다는 저자의 말이 정말 백번 맞다.</p>
<h4 id="비공개-코드에서-javadocs">비공개 코드에서 Javadocs</h4>
<p>Javadocs는 보통 시스템 내부에 속한 클래스와 함수를 설명하는 것이므로 비공개 코드에서는 Javadocs를 작성하지 마라.</p>
<h3 id="코드-적용">코드 적용</h3>
<h4 id="변경-전">변경 전</h4>
<p>주석에 이상한 링크가 첨부되어 있다. 둘 다 카메라, 갤러리와 관련된 정보가 담긴 링크다.
이런 링크를 애초에 달지 않는 편이 나은 것 같다. 내가 저 정보를 완벽하게 이해하고 코드에 적용한 후, 문서로 따로 정리를 했어야 했는데 대충 이해하고, 대충 적용하고, 정리하기 귀찮으니까 이런 식으로 작성해버린 거다.</p>
<pre><code>btnPhoto.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {
     // 사진 불러오기
     //https://crazykim2.tistory.com/487
    int writePermission = ContextCompat.checkSelfPermission(JoinActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
    int readPermission = ContextCompat.checkSelfPermission(JoinActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE);

    if(writePermission == PackageManager.PERMISSION_GRANTED &amp;&amp; readPermission == PackageManager.PERMISSION_GRANTED) {
      // 권한 허용 
      Intent intent = new Intent(Intent.ACTION_PICK);
      intent.setType(&quot;image/*&quot;);
      startActivityForResult(intent, 102);
    } else {
       requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 1000);
    }
      // https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;blogId=zoomen1004&amp;logNo=110183313246
      Log.e(&quot;print&quot;, &quot;카메라 화면 이동&quot;);
  }
});</code></pre><h4 id="변경-후">변경 후</h4>
<p>onClick 메서드를 오버라이딩한 것이라 이 부분만 봤을 땐 이 함수가 전체적으로 무슨 역할인지 알 수가 없다. 그래서 다음과 같은 부분을 변경했다.</p>
<ul>
<li>전체 함수를 설명하는 주석 추가</li>
<li>코드를 읽었을 때 직관적이지 않은 주요 로직 설명하는 주석 추가</li>
</ul>
<p>두번째 주석은 주요 로직을 함수로 떼어내어 주석을 삭제할 수 있을 것 같다.</p>
<pre><code>public setCameraIntent
btnPhoto.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {
     // 사진 불러오기
    int writePermission = ContextCompat.checkSelfPermission(JoinActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
    int readPermission = ContextCompat.checkSelfPermission(JoinActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE);

    // 권한 허용 여부 확인 -&gt; 이미지 pick
    if(writePermission == PackageManager.PERMISSION_GRANTED &amp;&amp; readPermission == PackageManager.PERMISSION_GRANTED) {
      Intent intent = new Intent(Intent.ACTION_PICK);
      intent.setType(&quot;image/*&quot;);
      startActivityForResult(intent, 102);
    } else {
       requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 1000);
    }
      Log.e(&quot;print&quot;, &quot;카메라 화면 이동&quot;);
  }
});</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Clean Code] 3장 함수]]></title>
            <link>https://velog.io/@sky-innerpeace/Clean-Code-3%EC%9E%A5-%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@sky-innerpeace/Clean-Code-3%EC%9E%A5-%ED%95%A8%EC%88%98</guid>
            <pubDate>Fri, 30 Sep 2022 15:18:32 GMT</pubDate>
            <description><![CDATA[<p>함수를 만들 때 유의해야 하는 여러 가지 규칙을 알아보자.</p>
<h3 id="작게-만들어라">작게 만들어라</h3>
<p>SOLID 원칙에도 비슷한 내용이 있다. 한 클래스는 하나의 책임만 가져야 한다. 함수든 클래스든 인터페이스든 적당한 규모로 자르는 것이 중요하다는 것을 모르는 개발자는 없을 것이다. 그 중에서도 함수는 프로그램의 가장 작은 단위로 자리 잡았고, 어떤 개발자에게 물어보든 긴 함수는 끔찍하다고 대답할 것이다.</p>
<p>그렇다면 어느 정도로 짧게 만들어야 할까?
들여쓰기로 생성되는 블록, 이라고 하면 여러 가지가 생각날 것이다. 쉽게 말해 한 함수에는 블록이 하나만 존재해야 한다.
if/else, while, for... 이 블록들은 특히나 중첩이 생기면 안 된다. 많이 양보해서 2단이면 되지 않겠냐고 저자는 말한다.</p>
<h3 id="한-가지만-해라">한 가지만 해라</h3>
<p>SOLID 원칙을 너무 빨리 꺼낸 것 같다. 아무래도 SRP 단일 책임 원칙은 이 부제에 조금 더 적합한 것 같다. 한 함수에 하나의 업무만 맡기라는 말은 많이 들은 것 같은데, 그 기준을 모르겠다는 게 문제다. 이때 제시하는 방법이 &#39;추상화 수준을 하나로 만들어라&#39;이다.</p>
<p>책에서는 <strong>내려가기 규칙</strong>을 적용한 코드를 작성하라고 하는데, 책을 읽듯 코드도 위에서 아래로 읽었을 때 자연스러워야 한다는 것이다. </p>
<ol>
<li>메인페이지에는 친구 목록과 인기글이 있어야 한다.
1-1. 이때 친한친구가 있다면 친한친구 목록을 위에 띄우고 그 아래에 일반 친구 목록을 띄운다.
1-2. 인기글은 본인 주변 사용자의 글만 보여준다.
1-2-1. 위치는 GPS로 재고 100m 이내에 있으면 주변이라 판단한다.
...</li>
</ol>
<p>마치 Top-Down 방식을 연상시킨다. 아래로 갈수록 점점 구체화되는 것이 특징이다. 이렇게 같은 추상화 수준을 유지하는 것들만 함수로 묶으면 된다.</p>
<h3 id="서술적인-이름을-사용하라">서술적인 이름을 사용하라</h3>
<p>이름이 짧고 의미 없는 것보다는 길게 서술하여 검색도 잘 되고 이해도 잘 되게 하는 것이 훨씬 좋다고 한다. 처음 코딩을 시작할 때부터 이름 짓기에 많은 시간을 허비(?)했다. 덕분에 지금은 좋은 변수 이름을 손민수하여 잘 쓰고 있기는 하지만, 좋은 이름을 짓는 건 매번 어려운 것 같다.</p>
<h3 id="함수-인수">함수 인수</h3>
<p>함수에서 이상적인 인수 개수는 0개라고 한다. (<del>지금까지 내가 쓴 코드는</del>)
함수에서 4개 이상의 인수는 어떠한 이유에서도 사용하면 안 된다고 한다.</p>
<h4 id="인수를-하나만-갖는-함수">인수를 하나만 갖는 함수</h4>
<ol>
<li>인수가 어떤 조건을 만족하는지 확인하는 함수
 ex) isOpen, isFunction ..</li>
<li>이벤트를 발생시키는 함수
 ex) handleClick, ...</li>
</ol>
<h4 id="플래그-인수">플래그 인수</h4>
<p>여러가지 기능을 처리하기 위해 플래그를 따로 받는 것은 시작부터 대놓고 여러 기능을 처리하겠다고 선언하는 것과 다름 없기 때문에 <strong>쓰지 않는 게 좋다.</strong></p>
<h4 id="인수-개수가-가변적인-함수">인수 개수가 가변적인 함수</h4>
<p>ex) String.format(&quot;%s worked %.2f hours.&quot;, name, hours);</p>
<h4 id="동사와-키워드">동사와 키워드</h4>
<ol>
<li>단항 함수는 함수, 인수가 모두 동사/명사 쌍을 이뤄야 함
 ex) write(name)</li>
<li>함수 이름에 키워드 추가하기<ul>
<li>다음와 같이 이름을 지으면 인수 순서를 기억할 필요가 없다.
ex) assertEquals → assertExpectedEqulasActual(expected, actual)</li>
</ul>
</li>
</ol>
<h3 id="부수-효과를-일으키지-마라">부수 효과를 일으키지 마라</h3>
<ul>
<li>함수에서 약속한 것을 넘어선 작업을 진행하면 대부분 시간적인 결합이나 순서 종속성을 초래한다.</li>
<li>함수 이름에서 나타나지 않는 부수적인 기능이 없도록 해야 한다.</li>
</ul>
<h3 id="명령과-조회를-분리하라">명령과 조회를 분리하라</h3>
<p>존재하는지 확인하고, 존재하면 명령하는 함수는 한 번에 작성하면 안 된다.</p>
<ul>
<li>변경 전<pre><code>if (set(&quot;username&quot;, &quot;unclebob&quot;))...</code></pre></li>
<li>변경 후<pre><code>if (attributeExists(&quot;username&quot;)) {
  setAttribute(&quot;username&quot;, &quot;unclebob&quot;);
  ...
}</code></pre><h3 id="오류-코드보다-예외를-사용하라">오류 코드보다 예외를 사용하라</h3>
</li>
<li>오류 코드를 반환하면 호출자는 오류 코드를 곧바로 처리해야 한다.</li>
<li>오류 처리 코드와 코드가 섞여 가독성이 떨어진다.<h4 id="trycatch-블록">Try/Catch 블록</h4>
이럴 때 사용하는 게 Try/Catch 블록이다. 이때 걱정되는 건 내가 만든 함수가 이런 식으로 지저분해진다는 것이다.<pre><code>try {
  // 내가 만든 함수
  deletePage(page);
  registry.deleteReference(page.name);
  configKeys.deleteKey(page.name.makeKey());
}
catch (Exception e) {
  logger.log(e.getMessage());
}</code></pre>try/catch문이 하나가 아니라면 전체적인 코드 구조가 눈에 들어오지 않는다.</li>
</ul>
<p>이때 저자는 내가 쓴 코드를 한 번 더 함수로 묶는 것을 권장한다.</p>
<pre><code>public void delete(Page page) {
    try {
        deletePageAndAllReferences(page); // 내가 작성했던 함수
    }
    catch (Exception e) {
        logError(e);
    }
}</code></pre><pre><code>private void deletePageAndAllReferences(page) throws Exception {
    deletePage(page);
    registry.deleteReference(page.name);
    configKeys.deleteKey(page.name.makeKey());
}
public void logError(Exception e) {
    logger.log(e.getMessage());
}</code></pre><h3 id="반복하지-마라">반복하지 마라</h3>
<p>같은 알고리즘이 반복되는 광경을 구경만 해서는 안 된다. 그 알고리즘을 고치기 위해 서로 다른 n개의 코드를 똑같이 고쳐야 한다면, 그것보다 귀찮은 일은 없을 것이다.</p>
<h3 id="느낀점-및-코드-소개">느낀점 및 코드 소개</h3>
<p>요즈음 스프링 스터디에서 자바를 다시 하는 탓에 정말 오랜만에 함수지향언어가 아닌 객체지향언어를 사용하여 코딩했다. 인공지능 때문에 파이썬 쓰고 근 1년 간 웹 개발만 주구장창 하느라 자바스크립트만 썼는데, 자바를 쓰니까 속이 후련해지는 기분이었다.
자바스크립트에서는 재사용성을 극대화할 수 있는 수단이 많지 않기 때문에(기껏해야 인터페이스지만 나는 그마저도 잘 안 쓴다) 이번에는 한 함수에 하나의 기능만 들어가야 한다는 말에 가장 집중했다.(자바스크립트에서도 적용하기 쉬울 것 같아서)</p>
<p>아래의 코드는 사용자가 폼을 제출할 때 실행되는 코드로, 사용자가 작성한 내용을 FormData 형식의 데이터 형식에 담아 서버에 post 요청을 보내는 코드다. 이때 &#39;한 함수는 하나의 기능만 해야 한다&#39;는 규칙이 깨져버린다. 바로 데이터 가공 + 데이터 전송 작업이 한 함수에 다 들어가있기 때문이다.</p>
<h4 id="변경-전-코드">변경 전 코드</h4>
<pre><code>const submit = async (values) =&gt; {
        const value = values || {};

        const frm = new FormData();
        const photoFile = document.getElementById(&quot;image&quot;);

        frm.append(&quot;image&quot;, photoFile.files[0]);
        frm.append(&quot;title&quot;, value.name);
        frm.append(&quot;placetype&quot;, value.place === &#39;offline&#39; ? true : false);
        frm.append(&quot;category&quot;, value.category);
        frm.append(&quot;content&quot;, value.content);
        frm.append(&quot;writer&quot;, userId);

        axios.post(&quot;/api/class/post&quot;, frm, {
            headers: {&quot;Content-Type&quot;: &quot;multipart/form-data&quot;},
        }).then((response) =&gt; {
          console.log(response);
          alert(&quot;글 작성이 완료되었습니다!&quot;);
          navigate(&quot;/&quot;);
        });
      };</code></pre><h4 id="변경-후-코드">변경 후 코드</h4>
<pre><code>    const createSubmitData = (data) =&gt; {
      const value = data || {};
      const frm = new FormData();
      const photoFile = document.getElementById(&quot;image&quot;);

      frm.append(&quot;image&quot;, photoFile.files[0]);
      frm.append(&quot;title&quot;, value.name);
      frm.append(&quot;placetype&quot;, value.place === &#39;offline&#39; ? true : false);
      frm.append(&quot;category&quot;, value.category);
      frm.append(&quot;content&quot;, value.content);
      frm.append(&quot;writer&quot;, userId);
    }

    const submit = async (values) =&gt; {
      const data = createSubmitData(values);
        axios.post(&quot;/api/class/post&quot;, data, {
            headers: {&quot;Content-Type&quot;: &quot;multipart/form-data&quot;},
        }).then((response) =&gt; {
          console.log(response);
          alert(&quot;글 작성이 완료되었습니다!&quot;);
          navigate(&quot;/&quot;);
        });
      };</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Clean Code] 2장 의미 있는 이름]]></title>
            <link>https://velog.io/@sky-innerpeace/Clean-Code-2%EC%9E%A5-%EC%9D%98%EB%AF%B8-%EC%9E%88%EB%8A%94-%EC%9D%B4%EB%A6%84</link>
            <guid>https://velog.io/@sky-innerpeace/Clean-Code-2%EC%9E%A5-%EC%9D%98%EB%AF%B8-%EC%9E%88%EB%8A%94-%EC%9D%B4%EB%A6%84</guid>
            <pubDate>Fri, 23 Sep 2022 05:46:17 GMT</pubDate>
            <description><![CDATA[<p>좋은 이름을 정의해보자.</p>
<h3 id="의도를-분명히-밝혀라">의도를 분명히 밝혀라</h3>
<p>변수 이름을 짓고, 옆에 주석을 달아야 이해가 되는 변수 이름은 의도가 제대로 담기지 않았다고 봐야 한다.</p>
<p>아래와 같이 딱 봤을 때 이해가 되어야 한다.</p>
<pre><code>int elapsedTimeInDays;
int daysSinceCreation;
int daySinceModification;</code></pre><p>예시 코드에 두들겨 맞고 말았다.</p>
<p>&lt;나쁜 코드&gt;</p>
<pre><code>public List&lt;int []&gt; getThem() {
    List&lt;int []&gt; list1 = new ArrayList&lt;int []&gt;();
    for(int [] x : the List)
        if(x[0] == 4)
            list.add(x);
        return list1;</code></pre><p>나만 이런 코드 짠 거 아니지
특히 알고리즘 문제 풀 때 이런 경우가 많을 것 같다. 물론 긴 문제가 아닌 이상 알고리즘에 네이밍 스킬까지 적용할 이유는 크지 않은 것 같다.</p>
<h3 id="그릇된-정보를-피하라">그릇된 정보를 피하라</h3>
<p>흔히 밈으로 &#39;우리는 이걸 00라고 부르기로 했어요&#39;라는 말이 있을 정도로 사회적으로 통용되는 단어는 굉장히 중요하다. 모두 사과를 보고 사과라고 부르는데, 나 혼자 오렌지라고 부르면 안 되니까.</p>
<p>이건 처음 들었는데, hp, aix, sco는 유닉스 플랫폼이나 유닉스 변종을 가리키는 이름이라 변수 이름으로 사용하면 안 된다고 한다.</p>
<p>그리고 실제 배열로 저장하는 게 아닌데 List라는 이름을 변수에 붙인다면, 그 코드를 보는 대부분이 오해할지도 모른다.</p>
<p>너무 비슷하게 생긴 긴~ 변수를 한 글자만 바꿔 다른 곳에서 또 사용한다면? 같은 변수라고 착각할지도 모른다.</p>
<h3 id="의미-있게-구분하라">의미 있게 구분하라</h3>
<p>저자는 &#39;불용어&#39;의 사용을 피하라고 말한다. (내가 제일 잘 하는 거다)
Product가 있고 ProductData, ProductInfo가 있다면 이 세 개는 어떤 차이가 있을까? 그냥 a product, the product라고 이름 짓는 거랑 크게 차이가 없는 거다.</p>
<h3 id="발음하기-쉬운-이름을-사용하라">발음하기 쉬운 이름을 사용하라</h3>
<p>이 책 초반을 세 번째 읽으면서 기억나는 거라곤 이 파트 뿐이다.</p>
<p>genymdhms</p>
<p>저자가 다니던 회사의 코드에 이런 해괴한 변수가 있다고 한다. 그리고 저자는 이를 &#39;젠 야 무다 함즈&#39; 라고 읽었다고 한다. 이 단어를 잊을 수가 없다.</p>
<h3 id="클래스--메서드-이름-짓기">클래스 / 메서드 이름 짓기</h3>
<ul>
<li><strong>클래스 이름</strong>과 객체 이름은 명사, 명사구</li>
<li><strong>메서드 이름</strong>은 동사, 동사구
  💫 get, is, set 등을 접두어로 붙이기도 한다.</li>
</ul>
<h3 id="한-개념에-한-단어를-사용하라">한 개념에 한 단어를 사용하라</h3>
<p>어떤 정보를 가져오는 여러 개의 함수가 있다고 가정하자. 이때 fetch, retrieve, get 등의 여러가지 단어를 써서 변수를 제각각 짓는다면 검색도 어렵고 기억하기는 더더욱 어려울 것이다.</p>
<p>비슷한 예시로는 controller, manager, driver가 있다.</p>
<h3 id="적용-코드">적용 코드</h3>
<pre><code>var p = intent.getStringExtra(&quot;poster&quot;)</code></pre><p>여기에서 p는? poster를 의미한다. 왜 줄여 썼을까.. 그렇게 귀찮았을까..(이 변수는 딱 2번 적는다)</p>
<pre><code>let LiRefsForQuiz = Array.from(Array(QUIZ_LENGTH), () =&gt; Array());</code></pre><p>이건 내가 쓴 코드가 아니긴 한데, LiRefsForQuiz는 Li는 list인 것으로 추정된다. 대충 퀴즈 리스트를 위한 ref라는 뜻이다.
결국 이 변수의 본질은 배열이니까, QuizRefList가 낫지 않을까 하는 생각을 해본다.</p>
<pre><code>setAllProducts(bagProducts.concat(hairBandProduct));
setProducts(bagProducts.concat(hairBandProduct));</code></pre><p>이런 코드도 있는데, 이 코드가 한 100번째 줄에 있고 실제 AllProducts와 Products의 차이는 500번째 줄 즈음에 나타난다. 처음 코드를 읽었을 때 참 당황스러웠다. 
알고보니 다음과 같았다.</p>
<ul>
<li>AllProducts : 모든 상품</li>
<li>Products : 선택된 카테고리에 해당하는 상품</li>
</ul>
<p>애초에 selectedProducts라고 이름을 지었다면 더 좋았을 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Clean Code] 1장 깨끗한 코드 ]]></title>
            <link>https://velog.io/@sky-innerpeace/Clean-Code-1%EC%9E%A5-%EA%B9%A8%EB%81%97%ED%95%9C-%EC%BD%94%EB%93%9C</link>
            <guid>https://velog.io/@sky-innerpeace/Clean-Code-1%EC%9E%A5-%EA%B9%A8%EB%81%97%ED%95%9C-%EC%BD%94%EB%93%9C</guid>
            <pubDate>Thu, 15 Sep 2022 14:14:33 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/sky-innerpeace/post/d48513af-414e-477c-a8bc-3a604f909528/image.jpg" alt=""></p>
<blockquote>
<p>세상에 나쁜 코드는 있다.</p>
</blockquote>
<h3 id="나쁜-코드">나쁜 코드</h3>
<p>책에서는 나쁜 코드 이야기를 꺼내며 80년대 후반 킬러 앱을 개발한 한 회사를 소개한다. 빠른 출시를 위해 마구잡이로 짠 코드로 인해 결국 망해버린 회사였다. 이렇게 극단적이지는 않으나 비슷한 사례를 우린 꽤 가까이에서, 자주 찾아볼 수 있다. 10년이 넘은 회사의 레거시 코드, 어디선가 들어본 적 있지 않은가? 예전에 한 학교 커뮤니티의 유지보수 업무를 맡은 적이 있는데, 무려 쇼핑몰 회사에서 10년 전에 작성한 php 코드였다. 문서화 하나 되지 않았던 그 코드는, 결국 아무도 건드릴 엄두를 못 내고 매번 인공호흡을 통해 살아나고 있다..</p>
<h3 id="문서화도-되지-않은-날-것의-레거시-코드">문서화도 되지 않은 날 것의 레거시 코드</h3>
<p>&#39;문서화되지 않은&#39; &#39;레거시&#39; 코드는 정말 최악이다. 레거시 코드를 다시 짜라는 업무를 받은 적도 있었다. 음.. 그런 걸 신입에게 왜 시킨 걸까? 물론 구조가 그리 복잡하진 않았지만.. 어쨌든 신입에게 그런 일을 맡기게 되면 일단 그 코드를 이해하는 것부터 시작한다. 정말, 정말정말 오래 걸린다.</p>
<h3 id="클린-코드를-작성하려면">클린 코드를 작성하려면</h3>
<p>책에서는 &#39;나쁜 코드를 작성하는 것은 전적으로 프로그래머의 잘못&#39;이라고 이야기한다. 일하다보면 마감기한 때문에 정말 습관처럼 나쁜 코드를 짜는 개발자들이 많으리라. 하지만 저자는 그것 자체가 잘못이라 이야기하는 것이다. 기한 안에 정확하게 일을 완수하는 가장 빠른 방법은 &#39;항상 코드를 깨끗하게 유지하는 것&#39;이라 이야기하고 있다.</p>
<h3 id="클린-코드란">클린 코드란?</h3>
<p>&#39;보기에 즐거운&#39; 코드, CPU 자원을 낭비하지 않는 코드, 세세하게 오류 처리한 코드, 메모리 누수, race condition이 없는 코드, 일관성 있는 명명법을 사용한 코드 등 다양한 예시를 들고 있다. 해결해야 하는 문제를 정확히 명명하고 이에 대한 명백한 해법을 제시하는 코드라고도 표현한다.</p>
<p>전체적으로 &#39;가독성&#39; 높은 코드를 클린 코드라고 하는 것 같다. </p>
<p>더해서 TDD(Test Driven Development) 이야기도 나온다. TDD에 관해서 10장짜리 정리 보고서를 만든 기억이 있는데, 참고한 책에서 TDD의 가장 큰 Risk는 대부분의 개발자가 TDD를 새로 배워야 한다는 점이라고 짚었다. 배우기만 하면, 그 효용은 훨씬 크다는 점을 강조했다. OTI 창립자 Dave Thomas는 테스트 케이스가 없는 코드는 클린 코드가 아니라고 지적했다. 추가로, 코드는 단위가 작아야 한다고도 지적했다.</p>
<p>Michael Feathers는 &#39;누군가 주의 깊게 짰다는 느낌&#39;이 드는 코드라고 이야기한다. </p>
<p>Ron Jeffries는 클린 코드의 비결을 &#39;중복 줄이기, 표현력 높이기, 작게 추상화하기&#39;라고 이야기한다. 여기에서 표현력에는 이름 짓기나 객체나 메서드를 적절하게 쪼개는 행위도 포함된다.</p>
<p>정리하면, 클린 코드를 짠다는 건 깨끗한 변수 이름, 깨끗한 함수, 깨끗한 클래스를 만드는 것이라고 볼 수 있다.  </p>
<h3 id="우리는-저자다">우리는 저자다</h3>
<p>읽기 쉬운 코드를 짜기는 힘들지만, 우리는 코드를 쓰는 내내 기존 코드를 읽는다. 즉, 조금 더 시간을 들여 읽기 쉬운 코드를 만든다면 이후 코드를 짜는 사람에게도 큰 도움이 될 것이라는 뜻이다. &#39;독자가 이해할 수 있는&#39; 코드를 짜자.</p>
<h3 id="나쁜-코드-예시">나쁜 코드 예시</h3>
<h4 id="1-나쁜-의도는-없었지만-깊은-중복의-늪">1. 나쁜 의도는 없었지만.. 깊은 중복의 늪</h4>
<image src="https://velog.velcdn.com/images/sky-innerpeace/post/cea42773-6a8f-4e05-9114-77e7f9b5d3b8/image.png" width=500px height=500px />
올해 초에 진행했던 프로젝트에서는 내가 코드리뷰를 담당했는데, 코드리뷰를 하지 않은 코드 중에 이런 코드가 있다. 조건문 안에 있는 index 값이 13까지 있는데 1부터 13까지 반복문을 돌며 저 코드가 반복된다. 심각한 중복이라고 볼  수 있다. 왜 이렇게 중복을 했냐라고 묻는다면 Modal 중간에 있는 visible 안에 들어있는게 state다. 저 때 마무리 작업을 위해 모였는데 30분만에 모달을 끼워넣어야 해서 다들 급하게 끼워넣다보니.. state를 무려 13개를 만들고 return도 13개하는 현상이 발생해버렸다.
코드리뷰를 안 한 내 탓이 크기는 하다.

<h4 id="2-fetch를-100번쯤-사용하면서-함수-추상화를-하지-않은-죄">2. fetch를 100번쯤 사용하면서 함수 추상화를 하지 않은 죄</h4>
<image src="https://velog.velcdn.com/images/sky-innerpeace/post/fd2fe14e-fb47-487b-87bd-7c5247b7bd3f/image.png" width=500px />
쓰다보니 fetch, axios 기계가 된 FE 개발자. 나뿐만은 아닐 거라 생각한다. 가장 뼈저리게 느꼈던 때는 해커톤이었다. 마지막 날 새벽에 교수님 피드백을 듣고(무박해커톤이었다) 페이지 두 개를 추가했고 6시간 동안 총 4개의 페이지를 완성하는 동안 fetch만 8번을 적었다. 그렇게 적는 동안 함수 추상화를 하지 않았다면.. 잘못이겠지.


<h4 id="3-무슨-의미를-가지고-있는지-모르겠는-변수-이름">3. 무슨 의미를 가지고 있는지 모르겠는 변수 이름</h4>
<image src="https://velog.velcdn.com/images/sky-innerpeace/post/00ce4888-90d5-4d8d-9886-25c386ec60a0/image.png" width=500px />
<image src="https://velog.velcdn.com/images/sky-innerpeace/post/35dad7e1-1ea0-4561-9843-ff1c0e2ed655/image.png" width=500px />

<p>이게 왜 아직도 padding89인지 알 수가 없다.. 이런 변수는 어디에든 보이면 일단 짜증부터 난다. <del>알밤아..</del></p>
<p>이외에도 함수 바로 위에 설명 주석은 지금까지 단 적이 없다. <del>그렇게 추천해줘도</del> 이것도 나쁜 코드의 예라고 할 수 있다.</p>
<h3 id="1장을-읽은-후기-✍">1장을 읽은 후기 ✍</h3>
<p>사실 나 이 책 1장은 벌써 세 번째 읽는다. 방학 때 일하러 오면 빌려 읽었던 것 같다. 이 말인 즉슨 나는 &#39;내 코드는 별로 좋은 코드가 아니&#39;라는 걸 알고 있지만 개선하는 방법은 아직도 모르는 애송이.. 인 것이다. 이번에 참여한 동아리 스터디에서 꼭! 클린 코드 짜는 구체적인 방법까지 알아가고 싶다. 그리고 각자 코드 얘기하는 것도 너무 재밌을 것 같다.</p>
<h3 id="📍-참고">📍 참고</h3>
<p>클린 코드(Clean Code) - 로버트 C.마틴</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Front-end] Semantic Tag(시맨틱 태그)]]></title>
            <link>https://velog.io/@sky-innerpeace/Semantic-Tag</link>
            <guid>https://velog.io/@sky-innerpeace/Semantic-Tag</guid>
            <pubDate>Wed, 31 Aug 2022 14:59:59 GMT</pubDate>
            <description><![CDATA[<p>매 개발마다 신경쓰겠다고 다짐하고 금방 잊어버리는 시맨틱 태그를 정리해봤다.
엘리님의 <a href="https://www.youtube.com/watch?v=T7h8O7dpJIg">유튜브</a> 영상을 참고했다. </p>
<h3 id="📍-semantic-tag-그게-뭐고-왜-써야-되는-건데">📍 Semantic Tag, 그게 뭐고 왜 써야 되는 건데?</h3>
<p>말 그대로 의미를 갖고 있는 태그다. 보통 홈페이지를 만들어보라고 하면 많은 사람들이 무한 div의 세계에 빠지고 마는데, 시맨틱 태그의 중요성에 대해 생각해보지 않아서 그런 경우가 많다. <del>물론 나도 회사에서 태그 교체 작업을 하기 전까지는 신경 쓰지 않았다.</del> Semantic tag는 SEO 측면에서 중요한데, 간단하게 말하면 검색엔진이 잘 알아듣도록 태그의 의미를 살려 코딩하라는 거다. SEO는 왜 중요하냐, 내가 만든 홈페이지를 원하는 사람이 비슷한 걸 검색했을 떄 적절하게 나타나도록 하는 방법이 SEO이기 때문이다. 태그 하나를 만들 때도 의미를 담아 정성스럽게 하자ㅏ.. 대충 이런 의미라면, 이러한 태그들에는 어떤 게 있을까?</p>
<h3 id="📍-전체-구조">📍 전체 구조</h3>
<p>HTML5부터는 아래 그림과 같이 시맨틱 태그를 적절히 활용하여 코딩하라-고 권고를 하는 것 같다. 전체적인 구조는 우리가 주로 보는 쇼핑몰 페이지에 대입해보면 이해하는 데 도움이 될 것이다.
<img src="https://velog.velcdn.com/images/sky-innerpeace/post/098431ff-e3e4-49bb-b38a-f26c28402698/image.png" alt=""></p>
<p style="text-align:center">출처 : W3Sschools</p>

<h3 id="📍-자주-헷갈리는-것들">📍 자주 헷갈리는 것들</h3>
<p>가장 상단에 header를 쓰고, 네비게이션 바는 nav 태그로 표시하고, 본문에는 section, article 등을 활용하고 사이드바 같은 게 있다면 aside를 활용하고, 가장 아래에 회사 정보 나타내는 바는 footer로 표시하라는 거잖아?</p>
<p>이쯤 이해하면 그 안에 자잘한 것들은 도대체 어떻게 쓰는 거지? 라는 생각이 들기 시작한다. 나도 정답을 다 아는 건 아니지만 참고한 영상에서 알려준 것들만 간단하게 정리해보자.</p>
<h4 id="❓-article-태그와-section-태그">❓ article 태그와 section 태그</h4>
<ul>
<li><p>article 태그 : main 태그 안에 있는 다른 정보들과 <strong>독립된 정보</strong>가 들어있는 부분을 묶을 때 사용
⭐article 태그 안에 있는 정보를 빈 페이지에 옮긴다고 상상해보자. 어색하지 않다면 사용해도 무방하다.</p>
</li>
<li><p>section 태그 : 연관되어 있는 정보들을 묶을 때 사용</p>
</li>
</ul>
<h4 id="❓-i-태그와-em-태그">❓ i 태그와 em 태그</h4>
<ul>
<li><p>i 태그 : 시각적으로만 이탤릭체</p>
</li>
<li><p>em 태그 : 강조의 의미를 담은 이탤릭체 변환 태그. strong 태그와 다르게 문장의 의미를 바꿔버린다.</p>
</li>
</ul>
<h4 id="❓-b-태그와-strong-태그">❓ b 태그와 strong 태그</h4>
<ul>
<li><p>b 태그 : 시각적으로만 볼드체</p>
</li>
<li><p>strong 태그 : 강조의 의미를 담은 볼드체, em과 다르게 문장의 의미에 영향을 끼치지 않는다.</p>
</li>
</ul>
<h4 id="❓-ol-vs-ul-vs-dl-태그">❓ ol vs ul vs dl 태그</h4>
<ul>
<li><p>ol(ordered list): 순서가 있는 목록을 나타냄</p>
</li>
<li><p>ul(unordered list): 순서가 없는 목록을 나타냄</p>
</li>
<li><p>dl(description list) : 단어+단어 설명을 나타낼 때 사용 (dt: 설명하려는 단어, dd: 단어의 설명)</p>
</li>
</ul>
<h3 id="참고">참고</h3>
<p><a href="https://www.youtube.com/watch?v=T7h8O7dpJIg">https://www.youtube.com/watch?v=T7h8O7dpJIg</a> ⭐강추
<a href="https://www.w3schools.com/html/html5_semantic_elements.asp">https://www.w3schools.com/html/html5_semantic_elements.asp</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Git] 특정 원격 저장소 브랜치를 지우는 법]]></title>
            <link>https://velog.io/@sky-innerpeace/Git-%EC%9B%90%EA%B2%A9-%EC%A0%80%EC%9E%A5%EC%86%8C-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EC%82%AD%EC%A0%9C</link>
            <guid>https://velog.io/@sky-innerpeace/Git-%EC%9B%90%EA%B2%A9-%EC%A0%80%EC%9E%A5%EC%86%8C-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EC%82%AD%EC%A0%9C</guid>
            <pubDate>Sun, 31 Jul 2022 13:57:06 GMT</pubDate>
            <description><![CDATA[<p>머지하는 과정에서 브랜치의 특정 부분이 머지가 안 됐다고 생각해서 급하게 revert를 했는데.. 아니었다. 그래서 그 브랜치를 다시 닫아야 하는데 문제는 원격 브랜치이기 때문에 내가 기존에 생각하는 방법으로는 불가능했다.</p>
<p>나는 로컬에서 지우려는 브랜치 쪽으로 이동해서 아래의 명령어를 입력하여 삭제했다.
<del>(브랜치 이동 없이도 가능할 것 같다.)</del></p>
<pre><code>git push origin --delete [브랜치명]</code></pre><p>github 원격 저장소에서 확인해보면 정상적으로 사라져 있다.</p>
<h4 id="참고">참고</h4>
<hr />
https://www.lesstif.com/gitbook/git-delete-remote-branch-20776547.html]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트 네이티브] 6장 리액트 네이티브 애니메이션]]></title>
            <link>https://velog.io/@sky-innerpeace/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%84%A4%EC%9D%B4%ED%8B%B0%EB%B8%8C-6%EC%9E%A5-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%84%A4%EC%9D%B4%ED%8B%B0%EB%B8%8C-%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98</link>
            <guid>https://velog.io/@sky-innerpeace/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%84%A4%EC%9D%B4%ED%8B%B0%EB%B8%8C-6%EC%9E%A5-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%84%A4%EC%9D%B4%ED%8B%B0%EB%B8%8C-%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98</guid>
            <pubDate>Tue, 03 May 2022 12:42:15 GMT</pubDate>
            <description><![CDATA[<h2 id="6-1-처음-만나는-리액트-네이티브-애니메이션">6-1. 처음 만나는 리액트 네이티브 애니메이션</h2>
<h4 id="📍-6장-프로젝트-준비">📍 6장 프로젝트 준비</h4>
<ul>
<li>src/copy/Person.tsx
useToggle : 커스텀 훅 사용 예정
deletePressed 속성 추가, Press Me 버튼 추가
IconText -&gt; Icon 컴포넌트로 변경<pre><code>import React, {useCallback, useState, useRef, useEffect, useMemo} from &#39;react&#39;
import type {FC} from &#39;react&#39;
import {View, Text, Image, Alert, Animated, Easing} from &#39;react-native&#39;
import {Colors} from &#39;react-native-paper&#39;
import Icon from &#39;react-native-vector-icons/MaterialCommunityIcons&#39;
import moment from &#39;moment-with-locales-es6&#39;
import * as D from &#39;../data&#39;
import {useToggle} from &#39;../hooks&#39; // 추가
import {Avatar} from &#39;../components&#39;
import {styles} from &#39;./Person.style&#39;
</code></pre></li>
</ul>
<p>moment.locale(&#39;ko&#39;)</p>
<p>export type PersonProps = {
  person: D.IPerson
  deletePressed: () =&gt; void // 추가
}</p>
<p>const Person: FC<PersonProps> = ({person, deletePressed}) =&gt; {
  // 변경 부분
  const avatarPressed = useCallback(() =&gt; Alert.alert(&#39;avatar pressed.&#39;), [])</p>
<p>  return (
    <View style={[styles.view]}>
      <View style={[styles.leftView]}>
        <Avatar
          imageStyle={[styles.avatar]}
          uri={person.avatar}
          size={50}
          onPress={avatarPressed}
        />
        <Text style={[styles.text]}>Press Me</Text>
      </View>
      <View style={[styles.rightView]}>
        <Text style={[styles.name]}>{person.name}</Text>
        <Text style={[styles.email]}>{person.email}</Text>
        <View style={[styles.dateView]}>
          <Text style={[styles.text]}>
            {moment(person.createdDate).startOf(&#39;day&#39;).fromNow()}
          </Text>
          <Icon
            name="trash-can-outline"
            size={26}
            color={Colors.lightBlue500}
            onPress={deletePressed}
          />
        </View>
        &lt;Text
          numberOfLines={3}
          ellipsizeMode=&quot;tail&quot;
          style={[styles.text, styles.comments]}&gt;
          {person.comments}
        </Text>
        &lt;Image style={[styles.image]} source={{uri: person.image}} /&gt;
        <View style={[styles.countsView]}>
          <Icon name="comment" size={24} color={Colors.blue500} />
          <Icon name="twitter-retweet" size={24} color={Colors.purple500} />
          <Icon name="heart" size={24} color={Colors.red500} />
        </View>
      </View>
    </View>
  )
}
export default Person</p>
<pre><code>
* src/copy/People.tsx
addPerson : 새로운 아이템이 가장 처음에 위치하도록 변경
useEffect로 초기 아이템 하나 기본적으로 추가
deletePerson : people에서 특정 id를 가진 사람 제거</code></pre><p>import React, {useState, useCallback, useEffect} from &#39;react&#39;
import {StyleSheet, View, Text, Switch, FlatList} from &#39;react-native&#39;
import {useTheme} from &#39;react-native-paper&#39;
import {useToggleTheme} from &#39;../contexts&#39;
import * as D from &#39;../data&#39;
import Person from &#39;./Person&#39;</p>
<p>export default function People() {
  const [people, setPeople] = useState&lt;D.IPerson[]&gt;([])
  const theme = useTheme()
  const toggleTheme = useToggleTheme()
  const addPerson = useCallback(() =&gt; {
    setPeople(people =&gt; [D.createRandomPerson(), ...people]) // 새로운 아이템이 가장 처음에 위치하도록 함
  }, [])
  const removeAllPersons = useCallback(() =&gt; {
    setPeople(notUsed =&gt; [])
  }, [])</p>
<p>  const deletePerson = useCallback(
    (id: string) =&gt; () =&gt;
      setPeople(people =&gt; people.filter(person =&gt; person.id != id)),
    []
  )
  useEffect(addPerson, []) // 초기 아이템 하나 추가</p>
<p>  return (
    &lt;View style={[styles.view, {backgroundColor: theme.colors.surface}]}&gt;
      &lt;View style={[styles.topBar, {backgroundColor: theme.colors.accent}]}&gt;
        <Text onPress={addPerson} style={styles.text}>
          add
        </Text>
        <Text onPress={removeAllPersons} style={styles.text}>
          remove all
        </Text>
        &lt;View style={{flex: 1}} /&gt;
        <Switch value={theme.dark} onValueChange={toggleTheme} />
      </View>
      &lt;FlatList
        data={people}
        renderItem={({item}) =&gt; (
          <Person person={item} deletePressed={deletePerson(item.id)} />
        )}
        keyExtractor={item =&gt; item.id}
      /&gt;
    </View>
  )
}
const styles = StyleSheet.create({
  view: {flex: 1},
  topBar: {flexDirection: &#39;row&#39;, padding: 5},
  text: {marginRight: 10, fontSize: 20}
})</p>
<pre><code>
* src/screens/MainNavigator.tsx</code></pre><p>import React, {useState} from &#39;react&#39;
import {BottomNavigation} from &#39;react-native-paper&#39;
import Basic from &#39;./Basic&#39;
import Monitor from &#39;./Monitor&#39;
import Toggle from &#39;./Toggle&#39;
import Interpolate from &#39;./Interpolate&#39;</p>
<p>export default function MainNavigator() {
  const [index, setIndex] = useState<number>(0)
  const [routes] = useState([
    {key: &#39;basic&#39;, title: &#39;Basic&#39;, icon: &#39;alpha-b-box&#39;},
    {key: &#39;monitor&#39;, title: &#39;Monitor&#39;, icon: &#39;eye-circle&#39;},
    {key: &#39;toggle&#39;, title: &#39;Toggle&#39;, icon: &#39;file-eye&#39;},
    {key: &#39;interpolate&#39;, title: &#39;Interpolate&#39;, icon: &#39;bullseye&#39;},
  ])
  const renderScene = BottomNavigation.SceneMap({
    basic: Basic,
    monitor: Monitor,
    toggle: Toggle,
    interpolate: Interpolate
  })
  return (
    &lt;BottomNavigation
      navigationState={{index, routes}}
      onIndexChange={setIndex}
      renderScene={renderScene}
    /&gt;
  )
}</p>
<pre><code>
#### 📍 리액트 네이티브 애니메이션 특징

1. 리액트 네이티브 애니메이션 모드
- **자바스크립트 엔진 애니메이션** : 자바스크립트 엔진이 기본으로 제공하는 requestAnimationFrame 함수를 사용한 애니메이션. 반복적으로 호출 시 다른 UI 컴포넌트의 동작이** 일시적으로 정지하는 현상**이 발생하여 지금은 잘 사용하지 않음.
- **⭐네이티브 모듈 애니메이션** : 자바나 Objective-C로 구현한 애니메이션.(useNativeDriver 값이 **true**일때 동작)

#### 📍 Animated가 제공하는 애니메이션</code></pre><p>import {Animated} from &#39;react-native&#39;</p>
<pre><code>![](https://velog.velcdn.com/cloudflare/sky-innerpeace/ccd1521c-861e-43eb-b817-e7b22914347d/image.png)

#### 📍 리액트 네이티브 애니메이션 요약

1. useRef훅을 사용하여 Animated.Value 클래스의 인스턴스 생성
리액트 네이티브 팀에서 useRef 훅 사용 권장</code></pre><p>const PersonBasic: FC<PersonProps> = ({person}) =&gt; {
  const animValue = useRef(new Animated.Value(0)).current</p>
<pre><code>
2. opacity 속성에 animValue 적용
Animated.Value 클래스의 인스턴스(animValue)는 항상 컴포넌트의 스타일 속성에 적용되어야 함!</code></pre><p>const PersonBasic: FC<PersonProps> = ({person}) =&gt; {
  const animValue = useRef(new Animated.Value(0)).current
  const rightViewAnimStyle = {opacity: animValue}</p>
<pre><code>
3. Animated.View 컴포넌트 사용
Animated.Value 타입을 View와 같은 컴포넌트에서 해석할 수 없으므로 View 대신 **Animated.View** 사용</code></pre><p>const PersonBasic: FC<PersonProps> = ({person}) =&gt; {
  const animValue = useRef(new Animated.Value(0)).current
  const rightViewAnimStyle = {opacity: animValue}
  &lt;Animated.View style={[styles.rightView, rightViewAnimStyle]}&gt;</p>
<pre><code>
4. 1초 동안 애니메이션 진행
animValue 값이 0(초깃값)부터 1(toValue)까지 1초(duration) 동안 보간하면서 애니메이션이 진행됨
* **보간** : 프레임을 일정 시간 동안 **연속**으로 보여주는 것</code></pre><p>const PersonBasic: FC<PersonProps> = ({person}) =&gt; {
  const animValue = useRef(new Animated.Value(0)).current
  const rightViewAnimStyle = {opacity: animValue}
  const onPress = () =&gt; [
      Animated.timing(animValue, {toValue: 1, useNativeDriver: true,
        duration: 1000}).start()
  }
  <Avatar uri={person.avatar} size={50| onPress={onPress} />
  &lt;Animated.View style={[styles.rightView, rightViewAnimStyle]}&gt;</p>
<pre><code>
* 완성된 **src/screens/PersonBasic.tsx**</code></pre><p>import React, {useCallback, useState, useRef} from &#39;react&#39;
import type {FC} from &#39;react&#39;
import {View, Text, Image, Alert, Animated} from &#39;react-native&#39;
import {Colors} from &#39;react-native-paper&#39;
import Icon from &#39;react-native-vector-icons/MaterialCommunityIcons&#39;
import moment from &#39;moment-with-locales-es6&#39;
import * as D from &#39;../data&#39;
import {Avatar} from &#39;../components&#39;
import {styles} from &#39;./Person.style&#39;</p>
<p>moment.locale(&#39;ko&#39;)</p>
<p>export type PersonProps = {
  person: D.IPerson
  deletePressed: () =&gt; void
}</p>
<p>const PersonBasic: FC<PersonProps> = ({person, deletePressed}) =&gt; {
  // Animated.Value 인스턴스를 생성하여 opacity에 적용
  const animValue = useRef(new Animated.Value(0)).current
  const rightViewAnimStyle = {opacity: animValue}
  // Animated.timing : 시간의 경과에 따라 애니메이션이 일어남
  const avatarPressed = useCallback(
    () =&gt;
      Animated.timing(animValue, {useNativeDriver: true, toValue: 1}).start(),
    []
  )
  return (
    <View style={[styles.view]}>
      <View style={[styles.leftView]}>
        <Avatar
          imageStyle={[styles.avatar]}
          uri={person.avatar}
          size={50}
          onPress={avatarPressed}
        />
        <Text style={[styles.text]}>Press Me</Text>
      </View>
      &lt;Animated.View style={[styles.rightView, rightViewAnimStyle]}&gt;
        <Text style={[styles.name]}>{person.name}</Text>
        <Text style={[styles.email]} onPress={avatarPressed}>
          {person.email}
        </Text>
        <View style={[styles.dateView]}>
          <Text style={[styles.text]}>
            {moment(person.createdDate).startOf(&#39;day&#39;).fromNow()}
          </Text>
          <Icon
            name="trash-can-outline"
            size={26}
            color={Colors.lightBlue500}
            onPress={deletePressed}
          />
        </View>
        &lt;Text
          numberOfLines={3}
          ellipsizeMode=&quot;tail&quot;
          style={[styles.text, styles.comments]}&gt;
          {person.comments}
        </Text>
        &lt;Image style={[styles.image]} source={{uri: person.image}} /&gt;
        <View style={[styles.countsView]}>
          <Icon name="comment" size={24} color={Colors.blue500} />
          <Icon name="twitter-retweet" size={24} color={Colors.purple500} />
          <Icon name="heart" size={24} color={Colors.red500} />
        </View>
      &lt;/Animated.View&gt;
    </View>
  )
}
export default PersonBasic</p>
<pre><code>
#### 📍 Animated.Value 클래스
해당 클래스는 애니메이션이 실행되면 값을 보간하는 number 타입 값을 value라는 속성에 저장하는 클래스. setValue를 통해 다른 값으로 변경 가능함.
</code></pre><p>const animValue = new Animated.Value(0)
animValue.setValue(100)</p>
<pre><code>animValue에 useRef 훅을 사용할 경우 animValue를 사용하는 컴포넌트가 재랜더링할 때마다 animValue가 새로 생성되는 불필요한 일이 발생하지 않으므로, 리액트 네이티브에서는 아래와 같은 useRef 사용을 권장하고 있다.</code></pre><p>const animValue = useRef(new Animated.Value(0)).current
animValue.setValue(100)</p>
<pre><code>이처럼 useRef를 사용하면 컴포넌트 초기 렌더링 시에만 유일하게 animValue가 생성된다.

#### 📍 useRef훅과 MutableRefObject 타입
* useRef 훅의 두가지 버전
RefObject&lt; T&gt; 타입 객체 반환
MutableRefObject&lt; T&gt; 타입 객체 반환

이때 MutableRefObject 제네릭 타입은 내부 속성인 current를 가지고 있고, 이 속성은 null이 허용되지 않는다.</code></pre><p>interface MutableRefObject<T> {
  current: T;
}</p>
<pre><code>따라서 아래의 예시 속 animValue는 null이 될 수 없고, 따라서 useMemo나 useCallback의 의존성 목록에 추가할 필요가 없다.</code></pre><p>const animValue = useRef(new Animated.Value(0)).current</p>
<pre><code>
#### 📍 Animated.View와 Animated.createAnimatedComponent 함수
Animated.createAnimatedComponent 함수는 다른 컴포넌트를 매개변수로 입력받아 Animated.Value 타입 객체를 처리할 수 있는 기능을 가진 새로운 컴포넌트를 만든다.</code></pre><p>type AnimatedComponent = Animated.createAnimatedComponent
export function createAnimatedComponent<T>(component: T): AnimatedComponent<T>;</p>
<pre><code>
이렇게 일일히 type을 새로 만들어주는 것은 번거로우므로 Animated에서는 앞서 사용했던 Animated.View 를 포함하여 Animated.Text, Animated.Image등의 컴포넌트를 제공한다. 애니메이션을 제공하는 기본 컴포넌트들이다.

#### 📍 Animated.timing
시간에 따른 기본적인 애니메이션을 제공하는 함수다. 지속 시간(duration)과 지연 시간(delay)를 설정하고 목표값(value)을 설정하면, 
1. 지연 시간이 지난 뒤 
2. 지속시간 동안 초기값에서 목표값까지 서서히 변화한다(=애니메이션)

이 함수의 매개변수는 value와 config로, value에는 초기값이 들어있고 config(Animated.TimingAnimationConfig 타입)에 아래와 같은 속성이 들어있다.
</code></pre><p>interface AnimationConfig {
  // 자바스크립트 엔진(false) 또는 네이티브 애니메이션(true) 사용 여부
  useNativeDriver: boolean;
}
interface TimingAnimationConfig extends AnimationConfig {
  toValue: number | Animated.Value // 목표 값 설정
  duration?: number // 애니메이션 진행 시간
  delay?: number // 애니메이션 진행 전 대기 시간
  easing?: (value: number) =&gt; number; //Easing이 제공하는 보간 함수 설정
}</p>
<pre><code>
#### 📍 Animated.CompositeAnimation 타입 객체
-&gt; Animated.timing이 반환하는 객체, 애니메이션 시작 및 종료를 담당한다.
애니메이션을 실행하는 start 메서드가 있고, 애니메이션이 종료되면 콜백함수를 남길 수 있습니다.
</code></pre><p>Animated.timing(animValue, {toValue:1, duration: 1000, useNativeDriver: true}).start(
    () =&gt; console.log(&#39;animation end&#39;)
}</p>
<pre><code>
📍 Press Me 글자를 클릭하면 애니메이션 효과 보이기
* src/screens/PersonMonitor.tsx</code></pre><p>import React, {useCallback, useState, useRef, useEffect, useMemo} from &#39;react&#39;
import type {FC} from &#39;react&#39;
import {View, Text, Image, Alert, Animated, Easing} from &#39;react-native&#39;
import {Colors} from &#39;react-native-paper&#39;
import Icon from &#39;react-native-vector-icons/MaterialCommunityIcons&#39;
import moment from &#39;moment-with-locales-es6&#39;
import * as D from &#39;../data&#39;
import {Avatar} from &#39;../components&#39;
import {Text as ThemeText, View as ThemeView} from &#39;../theme/paper&#39;
import {styles} from &#39;./Person.style&#39;</p>
<p>moment.locale(&#39;ko&#39;)</p>
<p>export type PersonProps = {
  person: D.IPerson
  deletePressed: () =&gt; void
}</p>
<p>const PersonMonitor: FC<PersonProps> = ({person, deletePressed}) =&gt; {
  const animValue = useRef(new Animated.Value(0)).current
  const [realAnimValue, setRealAnimValue] = useState<number>(0)
  const [animationEnd, setAnimationEnd] = useState<boolean>(false)
  useEffect(() =&gt; {
    const id = animValue.addListener((state: {value: number}) =&gt; {
      setRealAnimValue(state.value)
    })
    return () =&gt; animValue.removeListener(id)
  }, [])
  const avatarPressed = useCallback(
    () =&gt;
      Animated.timing(animValue, {
        toValue: 1,
        useNativeDriver: true,
        duration: 3000,
        easing: Easing.bounce
      }).start(() =&gt; setAnimationEnd(notUsed =&gt; true)),
    []
  )
  const rightViewAnimStyle = {opacity: animValue}
  return (
    <ThemeView>
      &lt;ThemeText style={[{fontSize: 20}]}&gt;
        realAnimValue: {realAnimValue}
      </ThemeText>
      &lt;ThemeText style={[{fontSize: 20}]}&gt;
        animationEnd: {animationEnd ? &#39;true&#39; : &#39;false&#39;}
      </ThemeText>
      <View style={[styles.view]}>
        <View style={[styles.leftView]}>
          <Avatar
            imageStyle={[styles.avatar]}
            uri={person.avatar}
            size={50}
            onPress={avatarPressed}
          />
          <Text style={[styles.text]}>Press Me</Text>
        </View>
        &lt;Animated.View style={[styles.rightView, rightViewAnimStyle]}&gt;
          <Text style={[styles.name]}>{person.name}</Text>
          <Text style={[styles.email]} onPress={avatarPressed}>
            {person.email}
          </Text>
          <View style={[styles.dateView]}>
            <Text style={[styles.text]}>
              {moment(person.createdDate).startOf(&#39;day&#39;).fromNow()}
            </Text>
            <Icon
              name="trash-can-outline"
              size={26}
              color={Colors.lightBlue500}
              onPress={deletePressed}
            />
          </View>
          &lt;Text
            numberOfLines={3}
            ellipsizeMode=&quot;tail&quot;
            style={[styles.text, styles.comments]}&gt;
            {person.comments}
          </Text>
          &lt;Image style={[styles.image]} source={{uri: person.image}} /&gt;
          <View style={[styles.countsView]}>
            <Icon name="comment" size={24} color={Colors.blue500} />
            <Icon name="twitter-retweet" size={24} color={Colors.purple500} />
            <Icon name="heart" size={24} color={Colors.red500} />
          </View>
        &lt;/Animated.View&gt;
      </View>
    </ThemeView>
  )
}
export default PersonMonitor</p>
<pre><code>
#### 📍 결과 화면
실행 도중의 사진
![](https://velog.velcdn.com/images/sky-innerpeace/post/e4b623a8-49bc-40d8-83c7-e2a1465cdf0a/image.png)


### 📍 토글 애니메이션 구현
초기 상태 -&gt; 애니메이션 진행 -&gt; 원래의 상태로 돌아감 : **애니메이션 순환 방식의 토글 애니메이션**

1. boolean타입의 started라는 변수를 생성
2. started 값이 false면 Animated.timing(animValue, {toValue: 1}) &lt;- 애니메이션을 진행
3. 애니메이션이 끝나면 started값이 true로 바뀜
=&gt; 애니메이션 순환

#### 📍 Animated.Value 클래스의 interpolate 메서드
![](https://velog.velcdn.com/images/sky-innerpeace/post/9fd973cf-29e8-486a-8e22-3022822c3332/image.png)
![](https://velog.velcdn.com/images/sky-innerpeace/post/3dfd6621-0bdd-47c3-af91-bfc8e2e2f65e/image.png)
![](https://velog.velcdn.com/images/sky-innerpeace/post/6c05a4f1-8887-49d6-a046-551fcfcbe9d5/image.png)
위 사례처럼 0~1 사이의 값으로 애니메이션 진행 척도를 조절하는데, 우리의 목표가 0과 1 사이에 없을 때가 더 많다.
이때는 입력 보간값을 새로운 보간값으로 변경하여야 한다.</code></pre><p>export class Value { 
  interpolate(config: InterpolationConfigType): AnimatedInterpolation;
}
class AnimatedInterpolation{
  interpolate(config: InterpolationConfigType): AnimatedInterpolation;
}</p>
<pre><code>interpolation 메서드는 InterpolationConfigType을 받아 AnimatedInterpolation 객체를 반환한다.
이때 InterpolationConfigType에 보간값에 관한 속성이 포함되어 있다.</code></pre><p>type ExtrapolateType = &#39;extend&#39; | &#39;identity&#39; | &#39;clamp&#39;;</p>
<p>type InterpolationConfigType = {
  inputRange: number[];
  outputRange: number[] | string[];
  easing?: (input: number) =&gt; number;
};</p>
<pre><code>
위의 세가지 사례를 InterpolationConfigType으로 표현해보자</code></pre><p>animValue.interpolate({inputRange: [0, 1], outputRange: [0, 100]})
animValue.interpolate({inputRange: [0, 1], outputRange: [&#39;red&#39;, &#39;blue&#39;]})
animValue.interpolate({inputRange: [0, 1], outputRange: [&#39;0deg&#39;, &#39;360deg&#39;]})</p>
<pre><code>
## 6-2. transform 스타일 속성에 적용하는 애니메이션

#### 📍 transform 스타일 속성 탐구
CSS의 transform과 같은 개념으로, 주의할 점은 속성을 적용한 컴포넌트의 레이아웃 위치(기존 위치)와 디스플레이 위치(transform하여 이동한 위치)가 다르다는 점이다. 
![](https://velog.velcdn.com/images/sky-innerpeace/post/4469eccd-b661-4ad2-9429-921f8287dde3/image.png)
rotate와 관련된 방향은 다음과 같다. (실제로 그렇지 않지만 그렇게 보이는 것도 있다.)
* rotate : 시계 방향
* rotateX : 아래에서 위
* rotateY : 왼쪽에서 오른쪽
* rotateZ : 시계 방향
![](https://velog.velcdn.com/images/sky-innerpeace/post/fc6d4732-55e6-4731-8a9a-8f7df0d5fdc8/image.png)

#### 📍 transform 스타일 속성에 애니메이션 적용하기
이전에 만들어둔 useStyle 커스텀 훅은 다음과 같다.
</code></pre><p>import {useMemo} from &#39;react&#39;</p>
<p>export const useStyle = (style: object, deps: any[] = []) =&gt; {
  return useMemo(() =&gt; style, deps)
}</p>
<pre><code>useStyle 훅을 사용하여 transform 스타일 속성이 있는 nameAnimStyle 스타일 객체 생성하고
수평 방향(translateX)으로 500픽셀 이동(interpolate)하고, 45도 회전하며, 크기가 두 배로 커지는 애니메이션을 구현해보자</code></pre><p>const nameAnimStyle = useStyle({
  transform: [
      {
      translateX: animValue.interpolate({
        inputRange: [0,1],
        outputRange: [0, 500]
      })
    },
   {
      rotate: animValue.interpolate({
        inputRange: [0,1],
        outputRange: [&#39;0deg&#39;, &#39;45deg&#39;]
      })
    },
   {
      scale: animValue.interpolate({
        inputRange: [0,1],
        outputRange: [1, 2]
      })
    },
  ]
})</p>
<pre><code>위의 코드를 커스텀 훅으로 구현해보자.
커스텀 훅 구현을 위해서는 아래와 같은 과정이 필요하다. 데이터를 다룰 때 흔히 사용하는 배열 속 딕셔너리로 만들어줘야 한다.</code></pre><p>const transform = {translateX, rotate, scale}
Object.keys(transform) // [&#39;translateX&#39;, &#39;rotate&#39;, &#39;scale&#39;]
    .map((key) =&gt; ({[key]: transform[key]})) // [{translateX: 0}, {rotate: &#39;0deg&#39;}, {scale: 1}</p>
<pre><code>
이걸 반영해서 useTransformStyle 커스텀 훅을 구현해본다.</code></pre><p>import {useStyle} from &#39;./useStyle&#39;</p>
<p>export const useTransformStyle = (
  transform: Record&lt;string, any&gt;,
  deps: any[] = []
) =&gt; {
  return useStyle(
    {
      transform: Object.keys(transform).map(key =&gt; ({[key]: transform[key]}))
    },
    deps
  )
}</p>
<pre><code>
#### 📍 애니메이션 연산 관련 함수
Animated는 +, -, *, /, % 등의 연산 관련 내장 함수가 있다.</code></pre><p>type Value = Animated.Value
export function add(a: Value, b: Value): Animated.AnimatedInterpolation // +
export function subtract(a: Value, b: Value): Animated.AnimatedInterpolation  // -
export function multiply(a: Value, b: Value): Animated.AnimatedInterpolation  // *
export function divide(a: Value, b: Value): Animated.AnimatedInterpolation  // /
export function modulo(a: Value, b: Value): Animated.AnimatedInterpolation  // %</p>
<pre><code>이때 매개변수 a,b에는 number 타입이 들어가면 안 된다.</code></pre><p>const [_10, set_10] = useState(new Animated.Value(10))
const [_20, set_20] = useState(new Animated.Value(20))
const textStyle = useStyle({
  fontSize: Animated.add(_10, Animated.multiply(animValue, _20)),
})</p>
<pre><code>
#### 📍 다시 사용할 수 있는 ImageSlider 컴포넌트 제작
- 이미지 슬라이더 또는 캐러셀(Carousel) 컴포넌트를 제작할 예정</code></pre><p><ImageSlider imageUrls={imageUrls} imageWidth={layout.width}
    showThumbnails={showThumbnails} /></p>
<pre><code>▶ ImageSlider에 들어가야 할 매개변수</code></pre><p>export type ImageSliderProps = {
  imageUrls: string[]  // 이미지의 URL이 있는 배경
  imageWidth: number  // 이미지 크기
  showThumbnails?: boolean  // 화면 아래에 썸네일 표시 여부
}</p>
<pre><code>▶ FlatList 코어 컴포넌트 관련 설정
* contentContainerStyle 속성(너비)에 &quot;이미지 개수 * 이미지 크기&quot; 만큼의 width 값을 주어야 함
* 좌우 스크롤로 현재 보는 이미지를 바꾸려면 scrollEnabled 속성을 true로 주어야 함
* showHorizontal ScrollIndicator 속성값에 false를 설정하면 스크롤 바 숨김
* scrollToIndex 메서드를 통해 해당 컴포넌트의 순서를 알아낼 수 있다(index값)

▶ FlatList의 scrollToIndex 메서드를 호출하는 코드</code></pre><p>export const ImageSlider: FC<ImageSliderProps> = ({
  imageUrls, imageWidth, showThumbNails}) =&gt; {
    const flatListRef = useRef&lt;FlatList | null&gt;(null)
    const selectImage = (index: number) =&gt; () =&gt; {
      flatListRef.current?.scrollToIndex({index})
    }
    return (<FlatList ref={flatListRef} />)
  }</p>
<pre><code>▶ 슬라이드 애니메이션 구현
Animated의 사칙 연산 함수를 사용하여 이미지 개수 만큼 나열하고, index번째 아이템에만 다른 색상으로 표시하는 슬라이드 애니메이션을 구현하도록 하겠다.
![](https://velog.velcdn.com/images/sky-innerpeace/post/71e5fb4c-d408-4ec9-8366-364393a269ba/image.png)
</code></pre><p>export const ImageSlider: FC<ImageSliderProps> = ({images, imageWidth, showThumbNails})
=&gt; {
  const circles = useMemo(() =&gt;
      images.map((uri, index) =&gt; <View key={index} style={styles.circle} />), [])
  &lt;View style={{flexDirection: &#39;row&#39;&#39;}}&gt;{circles}</View>
}</p>
<p>const styles = StyleSheet.create({
    circle: {width: circleWidth, height: circleWidth, borderRadius: circleWidth/2,
    marginRight: circleMarginRight, backgroundColor: Color.pink100},
})</p>
<pre><code>

▶ 최종 ImageSlider

* src/components/ImageSlider.tsx</code></pre><p>import React, {useRef, useMemo, useCallback} from &#39;react&#39;
import type {FC} from &#39;react&#39;
import {StyleSheet, FlatList, Image, View, Animated} from &#39;react-native&#39;
import type {NativeSyntheticEvent, NativeScrollEvent} from &#39;react-native&#39;
import {Colors} from &#39;react-native-paper&#39;
import {TouchableView} from &#39;./TouchableView&#39;
// prettier-ignore
import {useAnimatedValue, useMonitorAnimatedValue, useTransformStyle}
from &#39;../hooks&#39;</p>
<p>export type ImageSliderProps = {
  imageUrls: string[]
  imageWidth: number
  showThumbnails?: boolean
}
// prettier-ignore
const circleWidth = 10, circleMarginRight = 5, thumbnailSize = 30</p>
<p>export const ImageSlider: FC<ImageSliderProps> = ({
  imageUrls,
  imageWidth,
  showThumbnails
}) =&gt; {
  const flatListRef = useRef&lt;FlatList | null&gt;(null)
  const selectedIndexAnimValue = useAnimatedValue(0)
  const selectedIndex = useMonitorAnimatedValue(selectedIndexAnimValue)
  const circleWidthAnimValue = useAnimatedValue(circleWidth)
  const circleMarginRightAnimValue = useAnimatedValue(circleMarginRight)</p>
<p>  // contentOffset.x 값을 이미지로 나누어 현재 화면에 있는(스크롤된) 이미지의 index를 얻음
  const onScroll = useCallback( 
    (event: NativeSyntheticEvent<NativeScrollEvent>) =&gt; {
      if (imageWidth == 0) return
      const {contentOffset} = event.nativeEvent
      const index = Math.round(contentOffset.x / imageWidth)
      selectedIndexAnimValue.setValue(index)
    },
    [imageWidth]
  )
  const selectImage = useCallback(
    (index: number) =&gt; () =&gt; {
      // 썸네일 이미지를 눌렀을 때 Animated.View가 움직이도록 하는 코드
      selectedIndexAnimValue.setValue(index)
      flatListRef.current?.scrollToIndex({index}) // 이 코드 없어도 됨
    },
    []
  )
  const circles = useMemo(
    () =&gt;
      imageUrls.map((uri, index) =&gt; <View key={index} style={styles.circle} />),
    []
  )
  const thumbnails = useMemo(
    () =&gt;
      imageUrls.map((uri, index) =&gt; (
        &lt;TouchableView
          key={index}
          onPress={selectImage(index)}
          style={[
            styles.thumbnail,
            {
              borderColor:
                index == selectedIndex ? Colors.lightBlue900 : &#39;transparent&#39;
            }
          ]}&gt;
          &lt;Image
            source={{uri}}
            style={{width: thumbnailSize, height: thumbnailSize}}
          /&gt;
        </TouchableView>
      )),
    []
  )</p>
<p>  // 선택된 index 원 위치 찾기
  const translateX = useTransformStyle({ 
    translateX: Animated.multiply(
      selectedIndexAnimValue,
      Animated.add(circleWidthAnimValue, circleMarginRightAnimValue)
    )
  })</p>
<p>  return (
    &lt;&gt;
      &lt;FlatList
        ref={flatListRef}
        scrollEnabled={true}
        pagingEnabled={true}
        onScroll={onScroll}
        contentContainerStyle={{width: imageUrls.length * imageWidth}}
        showsHorizontalScrollIndicator={false}
        horizontal={true}
        data={imageUrls}
        renderItem={({item}) =&gt; (
          &lt;Image
            style={[styles.image, {width: imageWidth}]}
            source={{uri: item}}
          /&gt;
        )}
        keyExtractor={(item, index) =&gt; index.toString()}
      /&gt;
      &lt;View style={[styles.iconBar, {justifyContent: &#39;center&#39;}]}&gt;
        &lt;View style={{flexDirection: &#39;row&#39;}}&gt;
          {circles}
          &lt;Animated.View
            style={[styles.circle, styles.selectedCircle, translateX]}
          /&gt;
        </View>
      </View>
      {showThumbnails &amp;&amp; (
        &lt;View style={[styles.iconBar, {justifyContent: &#39;space-between&#39;}]}&gt;
          {thumbnails}
        </View>
      )}
    &lt;/&gt;
  )
}</p>
<p>const styles = StyleSheet.create({
  image: {height: 150, resizeMode: &#39;cover&#39;},
  iconBar: {flexDirection: &#39;row&#39;, padding: 5},
  thumbnail: {borderWidth: 1, padding: 2},
  circle: {
    width: circleWidth,
    height: circleWidth,
    borderRadius: circleWidth / 2,
    marginRight: circleMarginRight,
    backgroundColor: Colors.pink100
  },
  selectedCircle: {position: &#39;absolute&#39;, backgroundColor: Colors.pink700}
})</p>
<pre><code>
## 6-3. 여러 개의 애니메이션 한꺼번에 실행하기
#### 📍 Animated.CompositeAnimation[] 타입 객체를 매개변수로 하는 함수

* parallel, sequence, stagger 함수</code></pre><p>type CompositeAnimation = Animated.CompositeAnimation</p>
<p>export function sequence(
  animations: CompositeAnimation[]
): CompositeAnimation</p>
<p>export function parallel(
  animations: CompositeAnimation[],
  config?: {stopTogether?: boolean}
): CompositeAnimation</p>
<p>export function stagger(
  time: number,
  animations: CompositeAnimation[]
): COmpositeAnimation</p>
<pre><code>CompositeAnimation 타입은 Animated.timing 함수의 반환값이므로 아래와 같이 나타내면 CompositeAnimation[] 타입 객체 배열을 얻을 수 있다. 그리고 start 메서드를 이용해 CompositeAnimation 타입을 실행할 수 있다.</code></pre><p>// 객체 배열 얻기
const animations: CompositeAnimation[] = animValues.map(animValue =&gt;
    Animated.timing(animValue, {useNativeDriver:true, toValue: 1}) </p>
<p>// 배열 요소 각각을 애니메이션 실행
const animation: CompositeAnimation = Animated.sequence(animations)
animation.start()</p>
<pre><code>이때 **sequence 함수**는 animations에 담긴 애니메이션을 순서대로 실행한다. 주의할 점은 animValue는 원하는 개수만큼 독립적으로 생성해야 한다는 점이다.</code></pre><p>const animValues = useMemo(() =&gt; [1,2,3].map((notUsed) =&gt; new Animated.Value(0)), [])</p>
<pre><code>그리고 이렇게 각각 애니메이션을 주고 Animated.sequence로 실행시킨다.</code></pre><p>const leftIconStyle = useTransformStyle({
  translateX: interpolate(animValues[0], !started ? [-1200, 0] : [0, -1200])
})
const centerIconStyle = useTransformStyle({
  translateY: interpolate(animValues[1], !started ? [1200, 0] : [0, 1200])
})
const rightIconStyle = useTransformStyle({
  translateX: interpolate(animValues[2], !started ? [1200, 0] : [0, 1200])
})</p>
<View style={[styles.countsView]}>
  <AnimatedIcon name="comment" style={[leftIconStyle]} size={24} color={Colors.blue500} />
  <AnimatedIcon name="twitter-retweet" style={[centerIconStyle]} size={24} color={Colors.purple500} />
  <AnimatedIcon name="heart" size={24} style={[rightIconStyle]} color={Colors.red500} />
</View>
  ```

<h4 id="📍-animatedspring-함수">📍 Animated.spring 함수</h4>
<p>용수철 같은 보간을 만들어내는 함수</p>
<pre><code>export function spring(
  value: Animated.Value | Animated.ValueXY,
  config: SpringAnimationConfig
): Animated.CompositeAnimation</code></pre><p>Animated.timing에 easing을 bounce로 넣어주면 비슷해진다. 하지만 Animated.spring에는 <strong>duration이 없다.</strong></p>
<pre><code>Animated.timing(animValue, {
  useNativeDriver: true,
  toValue: !started ? 1 : 0,
  duration: 1000 * 1,
  easing: Easing.bounce
})</code></pre><p>최종구현은 아래와 같이 하면 된다. Animated.timing과 비슷하다.</p>
<pre><code>const fadeInStyle = useCallback(() =&gt; {
  Animated.spring(animValue, {toValue: 1, useNativeDriver: true}).start()
}, [])</code></pre><h4 id="📍-animatedparallel-함수">📍 Animated.parallel 함수</h4>
<p>여러 개의 애니메이션을 동시에 실행하는 것이 특징이다.</p>
<pre><code>export function parallel(animations: CompositeAnimation[],
  config?: {stopTogether?: boolean}): CompositeAnimation</code></pre><p>실습에서는 아바타를 클릭했을 때 콜백함수에 parallel 함수를 달아 동시 애니메이션을 실행시킨다.</p>
<pre><code>const avatarPressed = useCallback(() =&gt; {
  Animated.parallel(animations).start(toggleStarted)
}, [started])</code></pre><h4 id="📍-animateddelay와-animatedstagger-함수">📍 Animated.delay와 Animated.stagger 함수</h4>
<p>Animated.delay는 애니메이션 지연 시간을 나타내는 시간으로, Animated.stagger 함수도 delay를 입력 매개변수로 받는다. Animated.stagger 또한 애니메이션이 순서대로 진행된다.</p>
<pre><code>export namespace Animated {
  export const timing: (value: AnimatedValue, config: TimingAnimationConfig) =&gt;
    CompositeAnimation
  interface TimingAnimationConfig {
    delay?: number;
  }
}</code></pre><p>Animated.stagger 함수</p>
<pre><code>export function stagger(delay: number, animations: CompositeAnimation[]): CompositeAnimation</code></pre><p>Animated.sequence는 delay값이 있으면 애니메이션이 처음 시작하기 전에도 delay만큼 시간을 지연시킨 뒤 애니메이션을 실행시키지만, Animated.stagger는 첫 번째 애니메이션 실행 전에 지연 시간이 없다.</p>
<p>🌻 안드로이드에서는 현재 Animated.stagger 실행 시 애니메이션이 진행 중 멈추는 현상이 발생하므로 잘 사용하지 않는다.</p>
<h4 id="animatedloop-함수">Animated.loop 함수</h4>
<p>애니메이션 반복을 실행하는 함수.</p>
<pre><code>interface LoopAnimationConfig {
  iterations?: number        // 기본값 -1
  resetBeforeIteration?: Boolean    // 기본값 true
}

export function loop(animation: CompositeAnimation, config?: LoopAnimationConfig):
  CompositeAnimation</code></pre><p>이 함수로 무한 반복하는 애니메이션을 구현해보자. 위의 config를 명시하지 않으면 애니메이션이 무한 반복된다.</p>
<pre><code>const avatarPressed = useCallback(() =&gt; {
  if (Platform.OS === &#39;ios&#39;)
    Animated.loop(Animated.stagger(delay, [
      ...startAnimations,
      ...endAnimations
    ])).start()
  else Animated.loop(Animated.sequence([...startAnimations, ...endAnimations])).start()
}, [])</code></pre><h4 id="📍-enterexit-애니메이션-구현">📍 Enter/Exit 애니메이션 구현</h4>
<p>새로운 컴포넌트가 생성될 때 실행하는 Enter 애니메이션과 컴포넌트가 파괴될 때 실행하는 Exit 애니메이션을 구현해보자.</p>
<ul>
<li><p>enterAnimation 함수와 exitAnimation 함수</p>
<pre><code>const PersonEnterExit: FC&lt;PersonProps&gt; = ({person, onDelete}) =&gt; {
const [started, toggleStarted] = useToggle()

const enterAnimation = useCallback(() =&gt; {
  Animated.sequence([
  ]).start(toggleStarted)}, [])
const exitAnimation = useCallback(() =&gt; {
  Animated.sequence([
  ]).start(onDelete)}, [])</code></pre></li>
<li><p>반영된 코드</p>
<pre><code>import React, {useCallback, useMemo, useEffect} from &#39;react&#39;
import type {FC} from &#39;react&#39;
import {View, Text, Animated, Easing} from &#39;react-native&#39;
import {Colors} from &#39;react-native-paper&#39;
import Icon from &#39;react-native-vector-icons/MaterialCommunityIcons&#39;
import moment from &#39;moment-with-locales-es6&#39;
import * as D from &#39;../data&#39;
// prettier-ignore
import {useToggle, useTransformStyle, useAnimatedValues, useAnimatedValue,
useStyle} from &#39;../hooks&#39;
import {interpolate} from &#39;../utils&#39;
import {Avatar} from &#39;../components&#39;
import {styles} from &#39;./Person.style&#39;
</code></pre></li>
</ul>
<p>const AnimatedIcon = Animated.createAnimatedComponent(Icon)</p>
<p>export type PersonProps = {
  person: D.IPerson
  deletePressed: () =&gt; void
}
const PersonEnterExit: FC<PersonProps> = ({person, deletePressed}) =&gt; {
  const [started, toggleStarted] = useToggle()
  const opacityAnimValue = useAnimatedValue()
  const leftToRightAnimValue = useAnimatedValue()
  const topBottomAnimValue = useAnimatedValue()
  const iconAnimValues = useAnimatedValues(3)
  const iconAnimations = useMemo(
    () =&gt;
      iconAnimValues.map(animValue =&gt;
        Animated.spring(animValue, {
          useNativeDriver: true,
          toValue: !started ? 1 : 0
        })
      ),
    [started]
  )</p>
<p>  // 보간된 값 반영(enterAnimation, exitAnimation)
  // sequence 내에 timing, spring 함수를 사용하여 애니메이션 추가)
  const enterAnimation = useCallback(() =&gt; {
    Animated.sequence([
      Animated.timing(leftToRightAnimValue, {
        useNativeDriver: true,
        toValue: 1,
        duration: 1 * 1000,
        easing: Easing.bounce
      }),
      Animated.spring(opacityAnimValue, {useNativeDriver: true, toValue: 1}),
      Animated.timing(topBottomAnimValue, {
        useNativeDriver: true,
        toValue: 1,
        duration: 1 * 1000,
        easing: Easing.circle
      }),
      ...iconAnimations
    ]).start(toggleStarted)
  }, [])
  const exitAnimation = useCallback(() =&gt; {
    Animated.sequence([
      ...iconAnimations,
      Animated.parallel([
        Animated.spring(topBottomAnimValue, {
          useNativeDriver: true,
          toValue: 0
        }),
        Animated.spring(opacityAnimValue, {useNativeDriver: true, toValue: 0})
      ]),
      Animated.timing(leftToRightAnimValue, {
        useNativeDriver: true,
        toValue: 0,
        duration: 0.3 * 1000
      })
    ]).start(deletePressed)
  }, [started])
  useEffect(enterAnimation, []) // 애니메이션 자동 시작</p>
<p>  // 화면 왼쪽에서 들어오는 애니메이션, 오른쪽으로 나가는 애니메이션 구현
  const enterLeaveTransformStyle = useTransformStyle(
    {
      translateX: interpolate(
        leftToRightAnimValue,
        started ? [400, 0] : [-400, 0]
      )
    },
    [started]
  )
  const fadeInOutStyle = useStyle({
    opacity: opacityAnimValue
  })
  const topOrBottomTransformStyle = useTransformStyle(
    {
      translateY: interpolate(
        topBottomAnimValue,
        started ? [400, 0] : [-400, 0]
      )
    },
    [started]
  )
  const leftIconStyle = useTransformStyle({
    translateX: interpolate(
      iconAnimValues[0],
      !started ? [-1200, 0] : [0, -1200]
    )
  })
  const centerIconStyle = useTransformStyle({
    translateY: interpolate(iconAnimValues[1], !started ? [1200, 0] : [0, 1200])
  })
  const rightIconStyle = useTransformStyle({
    translateX: interpolate(iconAnimValues[2], !started ? [1200, 0] : [0, 1200])
  })</p>
<p>  return (
    &lt;Animated.View style={[styles.view, enterLeaveTransformStyle]}&gt;
      &lt;Animated.View style={[styles.leftView, fadeInOutStyle]}&gt;
        <Avatar imageStyle={[styles.avatar]} uri={person.avatar} size={50} />
      &lt;/Animated.View&gt;
      <View style={[styles.rightView]}>
        <Text style={[styles.name]}>{person.name}</Text>
        <Text style={[styles.email]}>{person.email}</Text>
        <View style={[styles.dateView]}>
          <Text style={[styles.text]}>
            {moment(person.createdDate).startOf(&#39;day&#39;).fromNow()}
          </Text>
          <Icon
            name="trash-can-outline"
            size={26}
            color={Colors.lightBlue500}
            onPress={exitAnimation}
          />
        </View>
        &lt;Text
          numberOfLines={3}
          ellipsizeMode=&quot;tail&quot;
          style={[styles.text, styles.comments]}&gt;
          {person.comments}
        </Text>
        &lt;Animated.Image
          style={[styles.image, fadeInOutStyle, topOrBottomTransformStyle]}
          source={{uri: person.image}}
        /&gt;
        <View style={[styles.countsView]}>
          <AnimatedIcon
            style={[leftIconStyle]}
            name="comment"
            size={24}
            color={Colors.blue500}
          />
          <AnimatedIcon
            style={[centerIconStyle]}
            name="twitter-retweet"
            size={24}
            color={Colors.purple500}
          />
          <AnimatedIcon
            style={[rightIconStyle]}
            name="heart"
            size={24}
            color={Colors.red500}
          />
        </View>
      </View>
    &lt;/Animated.View&gt;
  )
}
export default PersonEnterExit</p>
<pre><code>
## 6-4. PanResponder API 이해하기
* Panning : 영화나 사진 촬영을 할 때 고정된 위치에서 수평으로 카메라를 회전하는 기술
* FlatList에서 수평 스크롤 제스처를 가리켜 패닝이라고 한다.

#### 📍 ScrollEnabledProvider 컴포넌트</code></pre><p>import React, {createContext, useContext, useState} from &#39;react&#39;
import type {FC} from &#39;react&#39;</p>
<p>export type ScrollEnabledContextType = {
  scrollEnabled: boolean
  setScrollEnabled: (enable: boolean) =&gt; void
}
const defaultScrollEnabledContext = {
  scrollEnabled: true,
  setScrollEnabled: (enable: boolean) =&gt; {}
}
const ScrollEnabledContext = createContext<ScrollEnabledContextType>(
  defaultScrollEnabledContext
)
type ScrollEnabledContextProps = {}
export const ScrollEnabledProvider: FC<ScrollEnabledContextProps> = ({
  children
}) =&gt; {
  const [scrollEnabled, setScrollEnabled] = useState<boolean>(true)
  const value = {
    scrollEnabled,
    setScrollEnabled
  }
  // context.Provider 를 통해 context 하위에 상위 속성 공유
  return (
    &lt;ScrollEnabledContext.Provider value={value}&gt;
      {children}
    &lt;/ScrollEnabledContext.Provider&gt;
  )
}
export const useScrollEnabled = (): [boolean, (enabled: boolean) =&gt; void] =&gt; {
  const {scrollEnabled, setScrollEnabled} = useContext(ScrollEnabledContext)
  return [scrollEnabled, setScrollEnabled]
}</p>
<pre><code>
#### 📍 PanResponder API
react-native 패키지에서 제공하는 API.
사용을 위해서는 PanResponder.create 함수를 호출하여 PanResponder Instance 타입 객체를 얻어야 함.</code></pre><p>import type {PanResponderCallbacks, PanResponderInstance} from &#39;react-native&#39;</p>
<p>PanResponderObject = PanResponder.create(
  config: PanResponderCallbacks
): PanResponderInstance</p>
<pre><code>PanResponder.create 함수가 반환하는 PanResponder Instance 객체는 **panHandlers**라는 속성을 제공.</code></pre><p>import type {GestureResponderHandlers} from &#39;react-native&#39;</p>
<p>export interface PanResponderInstance {
  panHandlers: GestureResponderHandlers;
}</p>
<pre><code>panHandlers 속성을 사용한 코드</code></pre><p>const panResponder = PandResponder.create(...)
&lt;View {...panResponder.panHandlers} /&gt;</p>
<pre><code>
📍PanResponderCallbacks 타입
![](https://velog.velcdn.com/images/sky-innerpeace/post/26294ade-b5ec-4718-8c89-308ee215eecc/image.png)</code></pre><p>import type = {GestureResponderEvent, PanResponderGestureState) from &#39;react-native&#39;
type Event = GestureResponderEvent
type State = PanResponderGuestureState</p>
<p>export interface PanResponderCallbacks {
  onPanResponderGrant?: (e:Event, gestureState: State) =&gt; void;
  onPanRespponderMove?: (e:Event, gestureState: State) =&gt; void;
  onPanRespponderRelease?: (e:Event, gestureState: State) =&gt; void;
}</p>
<pre><code>해당 타입 중 onPanResponderGrant와 onPanResponderRelease가 실행되려면 아래의 메서드가 true를 반환해야 한다.</code></pre><p>export interface PanResponderCallbacks {
  onStartShouldSetPanResponder: (e:Event, s: State) =&gt; boolean,
}</p>
<pre><code>그리고 onPanResonderMove는 아래의 메서드가 true를 반환해야 이벤트 처리가 된다.</code></pre><p>export interface PanResponderCallbacks {
  onMoveShouldSetPanResponder: (e:Event, s: State) =&gt; boolean,
}</p>
<pre><code>
#### 📍 PanResponder API 를 활용한 코드</code></pre><p>import React, {useState} from &#39;react&#39;
import type {FC} from &#39;react&#39;
import {Platform, PanResponder} from &#39;react-native&#39;
// prettier-ignore
import type {GestureResponderEvent, PanResponderGestureState} from &#39;react-native&#39;
import {View, Text} from &#39;../theme/paper&#39;
import * as D from &#39;../data&#39;
import {useScrollEnabled} from &#39;../contexts&#39;</p>
<p>const ios = Platform.OS == &#39;ios&#39;</p>
<p>type Event = GestureResponderEvent
type State = PanResponderGestureState</p>
<p>export type PersonProps = {
  person: D.IPerson
  onDelete: () =&gt; void
}</p>
<p>const PersonPanRes: FC<PersonProps> = ({person, onDelete}) =&gt; {
  const [gestureState, setGestureState] = useState&lt;State | null&gt;(null)
  const [scrollEnabled, setScrollEnabled] = useScrollEnabled()
  const panResponder = PanResponder.create({
    // 여기가 true를 반환해야 onPanResponderGrant, onPanResponderRelease 메서드가 실행됨
    onStartShouldSetPanResponder() {
      return true
    },
    onPanResponderGrant(e: Event, s: State) {
      ios &amp;&amp; setScrollEnabled(false)
      setGestureState(s)
    },
    onPanResponderRelease(e: Event, s: State) {
      setGestureState(s)
      ios &amp;&amp; setScrollEnabled(true)
    },</p>
<pre><code>// 여기가 true를 반환해야 onPanResponderMove 메서드가 실행됨
onMoveShouldSetPanResponder() {
  return true
},
onPanResponderMove(e: Event, s: State) {
  setGestureState(s)
}</code></pre><p>  })
  return (
    &lt;View background style={[{width: &#39;100%&#39;}]}&gt;
      <Text>scrollEnabled: {scrollEnabled ? &#39;true&#39; : &#39;false&#39;}</Text>
      &lt;View accent {...panResponder.panHandlers} style={{height: 300, flex: 1}}&gt;
        {gestureState &amp;&amp; <Text>{JSON.stringify(gestureState, null, 2)}</Text>}
      </View>
    </View>
  )
}
export default PersonPanRes</p>
<pre><code>![](https://velog.velcdn.com/images/sky-innerpeace/post/1ea2d8c9-ec9c-4c32-a964-5a39fc1a4fd3/image.png)


#### 📍 PanResponderGestureState의 속성
주요 속성은 아래와 같다. 그 중에서도 dx, dy가 많이 사용된다고 한다.
![](https://velog.velcdn.com/images/sky-innerpeace/post/00383ee7-179c-49fd-9a97-ed48c27752c4/image.png)

#### 📍 컴포넌트 드래깅 기능 구현
(사진..불가능..실행이안되는)
동그란 아바타 컴포넌트 4개를 만들고 걔네를 드래그할 수 있게 하고, 드래깅하면서 이동한 거리를 확인할 수 있도록 하고자 한다. 이때 필요한 컴포넌트를 DragAvatar라고 정의한다. 동그란 아바타 컴포넌트를 말한다.

이때 필요한 개념들을 먼저 학습해보자.

▶ Animated.ValueXY 클래스
Animated.Value 타입 x와 y값을 가지는 클래스.
보통 translateX와 translateY에 Animated.ValueXY의 x,y값을 집어넣어서 애니메이션을 구현한다.</code></pre><p>export class ValueXY {
  x: Animated.Value
  y: Animated.Value
  constructor(valueIn?: {x: number | Animated.Value; y: number | Animated.Value})
  setValue(value: {x: number, y: number}): void
  extractOffset(): void
}</p>
<pre><code>여러 번 드래깅을 할 때 누적된 값을 계산하기 위해 ValueXY에서는 extractOffset 메서드를 제공한다.

컴포넌트를 드래깅할 때는 Animated.View를 조정한다. Animated.View의 style 속성 내에 translateX, translateY를 조절하여 드래깅 기능을 수행하도록 한다.</code></pre><p>const transformStyle = useTransformStyle({
    translateX: x값,
    translateY: y값
})</p>
<p>&lt;Animated.View style={[style, transformStyle]}&gt;
  <Avatar uri={avatarUrl} size={60} />
&lt;Animated.View&gt;</p>
<pre><code>translateX와 translateY 값을 바꿀 수 있기는 하지만, Animated.ValueXY 타입 변수의 실제 값을 바꿀 수는 없다는 게 문제다. 그래서 그 방법으로 **PanResponder**가 고안되었다.
</code></pre><p>const animValueXY = useAnimatedValueXY() // 우리가 조정해야 하는 Animated.ValueXY 타입 객체
const transformStyle = useTransformStyle({
  translateX: animValueXY.x,
  translateY: animValueXY.y
})</p>
<p>const panResponder = usePanResponder({
  onPanResponderMove(e: Event, s:State) {
      const {dx, dy} = s
    animValueXY.setValue({x: dx, y: dy})
  }
})</p>
<p>&lt;Animated.View style={[style, transformStyle]} {...panResponder.panHandlers}&gt;
  <Avatar uri={avatarUrl} size={60} />
&lt;Animated.View&gt;
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트 네이티브] 3장 컴포넌트 스타일링]]></title>
            <link>https://velog.io/@sky-innerpeace/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%84%A4%EC%9D%B4%ED%8B%B0%EB%B8%8C-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81</link>
            <guid>https://velog.io/@sky-innerpeace/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%84%A4%EC%9D%B4%ED%8B%B0%EB%B8%8C-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81</guid>
            <pubDate>Mon, 28 Mar 2022 16:43:47 GMT</pubDate>
            <description><![CDATA[<h2 id="3-1-style-속성과-stylesheet-api-이해하기">3-1. style 속성과 StyleSheet API 이해하기</h2>
<pre><code>import React from &#39;react&#39;
import { SafeAreaView, Text } from &#39;react-native&#39;

export default function App() {
    return () {
        &lt;SafeAreaView&gt;
        &lt;Text&gt;Hello world!&lt;/Text&gt;
         &lt;/SafeAreaView&gt;
    )
}</code></pre><h4 id="📍-스타일-속성">📍 스타일 속성</h4>
<ul>
<li>스타일 속성을 지정하는 방법 : 컴포넌트 안에 style 속성을 주고 그 안에 <strong>스타일 객체</strong>를 작성</li>
<li>이름 표기법
ⓐ 소문자로 시작 - flex, color
ⓑ 카멜 표기법 - 예시 : justifyContent, marginTop<pre><code>&lt;SafeAreaView style={{flex: 1, backgroundColor: &#39;blue&#39;}}&gt;</code></pre><h4 id="📍-스타일에-배열-설정-구문">📍 스타일에 배열 설정 구문</h4>
<pre><code>&lt;컴포넌트_이름 style={{스타일_객체1, 스타일_객체2, ...}} /&gt;</code></pre><h4 id="📍-color-vs-backgroundcolor">📍 color vs backgroundColor</h4>
</li>
<li>color : Text 컴포넌트에서 글자 색상을 변경</li>
<li>backgroundColor : &#39;View&#39;가 붙은 컴포넌트들에서 배경색 변경</li>
</ul>
<h4 id="📍-stylesheet-api">📍 StyleSheet API</h4>
<pre><code>import React from &#39;react&#39;
import {StyleSheet, SafeAreaView, Text} from &#39;react-native&#39;

export default function App() {
    return (
    &lt;SafeAreaView style={{styles.safeAreaView, {backgroundColor: &#39;blue&#39;}]}&gt;
        &lt;Text style={[styles.text, {color: &#39;white&#39;}]}&gt;
            Hello StyleSheet world!
        &lt;/Text&gt;
    &lt;/SafeAreaView&gt;
const styles = StyleSheet.create({
    safeAreaView: {flex:1, alignItems: &#39;center&#39;, justifyContent: &#39;center&#39;},
    text: {fontSize: 20}
})</code></pre><h2 id="3-2-view-컴포넌트와-css-박스-모델">3-2. View 컴포넌트와 CSS 박스 모델</h2>
<h4 id="📍-platform과-dimensions-api">📍 Platform과 Dimensions API</h4>
<ul>
<li>Platform : 빌드 타깃을 반환</li>
<li>Dimensions : 화면의 width, height 반환<pre><code>import {Platform, Dimensions} from &#39;react-native&#39;
console.log(Platform.OS) // &#39;android&#39; or &#39;ios&#39;
const {width, height} = Dimensions.get(&#39;window&#39;) // 화면의 width, height 반환</code></pre><h3 id="실습-화면">실습 화면</h3>
</li>
</ul>
<img src="https://images.velog.io/images/sky-innerpeace/post/3e9ecbea-2a23-490e-8269-5835c3e81ab2/image.png" width=300px />

<pre><code>import React from &#39;react&#39;;
// prettier-ignore
import {Platform, StyleSheet, SafeAreaView, Text, Dimensions} from &#39;react-native&#39;;
import {Colors} from &#39;react-native-paper&#39;;

const {width, height} = Dimensions.get(&#39;window&#39;);

export default function App() {

  return (
    &lt;SafeAreaView style={[styles.safeAreaView]}&gt;
      &lt;Text style={[styles.text]}&gt;os: {Platform.OS}&lt;/Text&gt;
      &lt;Text style={[styles.text]}&gt;width: {width}px&lt;/Text&gt;
      &lt;Text style={[styles.text]}&gt;height: {height}px&lt;/Text&gt;
    &lt;/SafeAreaView&gt;
  );
}
const styles = StyleSheet.create({
  safeAreaView: {backgroundColor: Colors.blue500, height:height},
  text: {fontSize: 20, color: Colors.blue200},
});</code></pre><h4 id="📍-width-height-설정법">📍 width, height 설정법</h4>
<p><strong>1) 명시적으로 width, height를 설정하지 않고 리액트 네이티브의 기본 설정 방식을 따르는 방법</strong></p>
<ul>
<li>width는 부모 컴포넌트를, height는 자식요소가 수평 정렬 시에는 가장 높은 값, 수직 정렬 시에는 다 더한 값을 따름</li>
</ul>
<p><strong>2) 픽셀(px) 단위의 숫자 직접 설정하는 방법</strong>
<strong>3) 부모 요소의 width, height를 기준으로 자식 컴포넌트의 크기를 퍼센트로 정하는 방법</strong>
<strong>4) flex 속성을 사용해 여러 자식 컴포넌트가 부모 컴포넌트의 크기를 나눠 가지는 방법</strong></p>
<ul>
<li>flex 스타일 속성: 보통 0 ~ 1의 값을 가짐(0~ 100%로 치환) 1이 넘어가면 남은 여백을 비율대로 나눠 가짐</li>
</ul>
<h4 id="📍-margin-padding-border">📍 margin, padding, border</h4>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/9a1510d5-1bda-45a1-97e1-a4322602dce4/image.png" alt="">
웹 <strong>개발자 도구 - 요소 - 스타일</strong>에서 이런 그림을 많이 봤을 것이다. <strong>margin</strong>은 컴포넌트 간의 간격, <strong>padding</strong>은 컴포넌트 내의 공백을 의미한다. <strong>border</strong>는 각 컴포넌트를 감싸는 테두리라고 생각하면 된다.</p>
<p><strong>각 속성값</strong></p>
<ul>
<li><strong>margin</strong> : marginTop, marginRight, marginBottom, marginLeft</li>
<li><strong>padding</strong> : paddingTop, paddingRight, paddingBottom, paddingLeft</li>
<li><strong>border</strong> : borderWidth, borderColor, borderRadius, borderStyle</li>
</ul>
<h3 id="실습-화면-1">실습 화면</h3>
<img src="https://images.velog.io/images/sky-innerpeace/post/e49510b4-d9b6-46c2-83b9-86461d6aecfb/image.png" />

<pre><code>import React from &#39;react&#39;;
// prettier-ignore
import {Platform, StyleSheet, SafeAreaView, Text, Dimensions, View} from &#39;react-native&#39;;
import {Colors} from &#39;react-native-paper&#39;;

const {width, height} = Dimensions.get(&#39;window&#39;);

export default function App() {
  return (
    &lt;SafeAreaView style={[styles.safeAreaView]}&gt;
      &lt;Text style={[styles.text]}&gt;os: {Platform.OS}&lt;/Text&gt;
      &lt;Text style={[styles.text]}&gt;width: {width}px&lt;/Text&gt;
      &lt;Text style={[styles.text]}&gt;height: {height}px&lt;/Text&gt;

      &lt;View style={[styles.box, styles.border]} /&gt;
      &lt;View style={[styles.box, styles.border, {borderRadius: 20}]} /&gt;
      &lt;View
        style={[
          styles.box, styles.border,
          {borderTopLeftRadius: 40, borderBottomLeftRadius: 40}]} /&gt;
    &lt;/SafeAreaView&gt;
  );
}
const styles = StyleSheet.create({
  safeAreaView: {backgroundColor: Colors.blue500, flex: 1, padding: 10},
  text: {marginBottom: 10, fontSize: 20, color: Colors.blue200},
  box: {height: 100, backgroundColor: Colors.lime500, marginBottom: 10},
  border: {borderWidth: 10, borderColor: Colors.blue900},
});
</code></pre><h4 id="❓-안드로이드와-ios에서-동일하게-적용이-되지-않을-때">❓ 안드로이드와 iOS에서 동일하게 적용이 되지 않을 때</h4>
<pre><code>속성: Platform.select({
    ios: Platform.OS가 ios일 때의 값,
    android: Platform.OS가 android일 때의 값
})</code></pre><h2 id="3-3-자원과-아이콘-사용하기">3-3. 자원과 아이콘 사용하기</h2>
<h4 id="📍-image-imagebackground를-사용하여-이미지와-배경이미지-나타내기">📍 Image, ImageBackground를 사용하여 이미지와 배경이미지 나타내기</h4>
<h4 id="실습-화면-2">실습 화면</h4>
<img src="https://images.velog.io/images/sky-innerpeace/post/c82e56d2-91a9-4a17-bd85-48cbde9e0cfb/image.png" />


<pre><code>import React from &#39;react&#39;;
import * as D from &#39;./src/data&#39;;
// prettier-ignore
import { StyleSheet, SafeAreaView, Image, ImageBackground, Text } from &#39;react-native&#39;;

const avatarUrl = D.randomAvatarUrl();
const avatarSize = 50;

export default function App() {
  return (
    &lt;SafeAreaView style={[styles.flex]}&gt;
      &lt;ImageBackground
        style={[styles.flex, styles.imageBackground]}
        source={require(&#39;./src/assets/images/bg.jpg&#39;)}&gt;
        &lt;Image source={{uri: avatarUrl}} style={[styles.image]} /&gt;
      &lt;/ImageBackground&gt;
    &lt;/SafeAreaView&gt;
  );
}

const styles = StyleSheet.create({
  flex: {flex: 1},
  imageBackground: {padding: 10},
  image: {width: avatarSize, height: avatarSize, borderRadius: avatarSize / 2},
});</code></pre><ul>
<li>로컬에 존재하는 이미지는 require 메서드로 불러옴</li>
<li>Uri를 받아오고 싶을 땐 {{uri: 링크}}의 형식으로 받아 옴</li>
<li><strong>Url</strong>은 내가 찾고자 하는 파일의 <strong>고유한 위치</strong>를 나타내고 <strong>Uri</strong>는 인터넷 상에서 특정 자원(파일)을 나타내는 유일한 <strong>식별자</strong> 값을 나타낸다.</li>
</ul>
<h4 id="📍-폰트-사용하기">📍 폰트 사용하기</h4>
<p><a href="https://fonts.google.com/specimen/Dancing+Script">예시 폰트 다운 링크</a></p>
<p><strong>react-native.config.js</strong></p>
<pre><code>module.exports = {
  project: {
    ios: {},
    android: {},
  },
  assets: [&#39;./src/assets/fonts&#39;],
}</code></pre><p>project키를 다음과 같이 설정해주고 react-native link 명령어를 입력하면 위에서 작성한 js파일이 프로젝트에 반영된다.</p>
<pre><code>npx react-native link</code></pre><p>본 명령어를 실행하고 나면 <strong>src/assets/fonts</strong> 디렉터리의 파일이 <strong>android/app/src/main/assets/fonts</strong> 디렉터리에 복사됨을 알 수 있다.</p>
<h4 id="실습-화면-3">실습 화면</h4>
<img src="https://images.velog.io/images/sky-innerpeace/post/84687b5e-9e66-45e7-8200-ed04a54d7e68/image.png" />

<pre><code>import React from &#39;react&#39;;
import * as D from &#39;./src/data&#39;;
// prettier-ignore
import { StyleSheet, SafeAreaView, Image, ImageBackground, Text, Platform, View } from &#39;react-native&#39;;

const avatarUrl = D.randomAvatarUrl();
const avatarSize = 50;

const text = `Almost before we knew it, we had left the ground.`

export default function App() {
  return (
    &lt;SafeAreaView style={[styles.flex]}&gt;
      &lt;ImageBackground
        style={[styles.flex, styles.imageBackground]}
        source={require(&#39;./src/assets/images/bg.jpg&#39;)}&gt;
        &lt;Image source={{uri: avatarUrl}} style={[styles.image]} /&gt;
        &lt;View style={[styles.flex, styles.padding10]}&gt;
          &lt;Text style={[styles.text, styles.regular]}&gt;{text} [regular]&lt;/Text&gt;
          &lt;Text style={[styles.text, styles.medium]}&gt;{text} [medium]&lt;/Text&gt;
          &lt;Text style={[styles.text, styles.semiBold]}&gt;{text} [semi bold]&lt;/Text&gt;
          &lt;Text style={[styles.text, styles.bold]}&gt;{text} [bold]&lt;/Text&gt;
        &lt;/View&gt;
      &lt;/ImageBackground&gt;
    &lt;/SafeAreaView&gt;
  );
}

const styles = StyleSheet.create({
  flex: {flex: 1},
  imageBackground: {padding: 10},
  image: {width: avatarSize, height: avatarSize, borderRadius: avatarSize / 2},
  padding10: {padding: 10},
  text: {textAlign: &#39;center&#39;, fontSize: 25, color: &#39;white&#39;, marginBottom: 10},

  regular: {fontFamily: &#39;DancingScript-Regular&#39;, fontWeight: &#39;400&#39;},
  medium: {fontFamily: &#39;DancingScript-Medium&#39;, fontWeight: &#39;500&#39;},
  semiBold: {fontFamily: &#39;DancingScript-SemiBold&#39;, fontWeight: &#39;600&#39;},
  bold: {
    fontFamily: &#39;DancingScript-Bold&#39;,
    fontWeight: Platform.select({ios: &#39;700&#39;, android: &#39;600&#39;})
  }
});</code></pre><h4 id="📍-아이콘-사용하기">📍 아이콘 사용하기</h4>
<p>react-native-vector-icons 패키지를 설치한 후 사용하기 위해서는 아래의 명령어 실행</p>
<pre><code>npx react-native link react-native-vector-icons</code></pre><h4 id="실습-화면-4">실습 화면</h4>
<img src="https://images.velog.io/images/sky-innerpeace/post/1736b00d-d839-4030-a204-f6d1f84cda6c/image.png" />


<pre><code>import React from &#39;react&#39;;
import * as D from &#39;./src/data&#39;;
// prettier-ignore
import { StyleSheet, SafeAreaView, Image, ImageBackground, Text, Platform, View, Alert } from &#39;react-native&#39;;
import {Colors} from &#39;react-native-paper&#39;;
import Icon from &#39;react-native-vector-icons/MaterialCommunityIcons&#39;;
const avatarUrl = D.randomAvatarUrl();
const avatarSize = 50;

const text = &#39;Almost before we knew it, we had left the ground.&#39;;
const onIconPressed = () =&gt; Alert.alert(&#39;icon pressed&#39;);

export default function App() {
  return (
    &lt;SafeAreaView style={[styles.flex]}&gt;
      &lt;ImageBackground
        style={[styles.flex, styles.imageBackground]}
        source={require(&#39;./src/assets/images/bg.jpg&#39;)}&gt;
        &lt;Image source={{uri: avatarUrl}} style={[styles.image]} /&gt;
        &lt;View style={[styles.flex, styles.padding10]}&gt;
          &lt;Text style={[styles.text, styles.regular]}&gt;{text} [regular]&lt;/Text&gt;
          &lt;Text style={[styles.text, styles.medium]}&gt;{text} [medium]&lt;/Text&gt;
          &lt;Text style={[styles.text, styles.semiBold]}&gt;{text} [semi bold]&lt;/Text&gt;
          &lt;Text style={[styles.text, styles.bold]}&gt;{text} [bold]&lt;/Text&gt;
        &lt;/View&gt;
        &lt;Icon
          name=&quot;home&quot;
          size={50}
          color={Colors.lightBlue500}
          onPress={onIconPressed}
        /&gt;
      &lt;/ImageBackground&gt;
    &lt;/SafeAreaView&gt;
  );
}

const styles = StyleSheet.create({
  flex: {flex: 1},
  imageBackground: {padding: 10},
  image: {width: avatarSize, height: avatarSize, borderRadius: avatarSize / 2},
  padding10: {padding: 10},
  text: {textAlign: &#39;center&#39;, fontSize: 25, color: &#39;white&#39;, marginBottom: 10},

  regular: {fontFamily: &#39;DancingScript-Regular&#39;, fontWeight: &#39;400&#39;},
  medium: {fontFamily: &#39;DancingScript-Medium&#39;, fontWeight: &#39;500&#39;},
  semiBold: {fontFamily: &#39;DancingScript-SemiBold&#39;, fontWeight: &#39;600&#39;},
  bold: {
    fontFamily: &#39;DancingScript-Bold&#39;,
    fontWeight: Platform.select({ios: &#39;700&#39;, android: &#39;600&#39;}),
  },
});</code></pre><h2 id="3-4-컴포넌트-배치-관련-스타일-속성-탐구하기">3-4. 컴포넌트 배치 관련 스타일 속성 탐구하기</h2>
<h4 id="실습-화면-5">실습 화면</h4>
<img src="https://images.velog.io/images/sky-innerpeace/post/7254fc04-73e9-4569-9e11-49446c693646/image.png" />


<p><strong>Content.tsx</strong> - TopBar, BottomBar도 title만 다름</p>
<pre><code>import React from &#39;react&#39;;
import {StyleSheet, View, Text} from &#39;react-native&#39;;
import {Colors} from &#39;react-native-paper&#39;;
import * as D from &#39;../data&#39;;

const title = &#39;Content&#39;;
export default function Content() {
  return  (
    &lt;View style={[styles.view]}&gt;
      &lt;Text style={[styles.text]}&gt;{title}&lt;/Text&gt;
    &lt;/View&gt;
  );;
}

const styles = StyleSheet.create({
  view: {padding: 5, backgroundColor: Colors.blue900},
  text: {fontSize: 20, color: &#39;white&#39;},
});</code></pre><p><strong>App.tsx</strong></p>
<pre><code>import React from &#39;react&#39;;
import {SafeAreaView, StyleSheet} from &#39;react-native&#39;;
import {Colors} from &#39;react-native-paper&#39;;
import TopBar from &#39;./src/screens/TopBar&#39;;
import Content from &#39;./src/screens/Content&#39;;
import BottomBar from &#39;./src/screens/BottomBar&#39;;

export default function App() {
  return (
    &lt;SafeAreaView style={styles.flex}&gt;
      &lt;TopBar /&gt;
      &lt;Content /&gt;
      &lt;BottomBar /&gt;
    &lt;/SafeAreaView&gt;
  );
}

const styles = StyleSheet.create({
  flex: {flex: 1, backgroundColor: Colors.lightBlue100},
});</code></pre><h4 id="❓-bottombar-아래에는-safeareaview만-있는-이유">❓ BottomBar 아래에는 SafeAreaView만 있는 이유?</h4>
<p>SafeAreaView 내의 컴포넌트들은 flex값이 설정되어있지 않다. 따라서 flex를 1로 주었던 SafeAreaView만 화면 전체에 깔리게 되었다.</p>
<ul>
<li>Content에 <strong>flex:1</strong> 을 부여하는 것과 <strong>height:100%</strong>를 부여하는 것의 차이</li>
</ul>
<pre><code>import React from &#39;react&#39;;
import {StyleSheet, View, Text} from &#39;react-native&#39;;
import {Colors} from &#39;react-native-paper&#39;;
import * as D from &#39;../data&#39;;

const title = &#39;Content&#39;;
export default function TopBar() {
  return (
    &lt;View style={([styles.view], {flex: 1})}&gt;
      &lt;Text style={[styles.text]}&gt;{title}&lt;/Text&gt;
    &lt;/View&gt;
  );
}

const styles = StyleSheet.create({
  view: {padding: 5, backgroundColor: Colors.blue900},
  text: {fontSize: 20, color: &#39;white&#39;},
});
</code></pre><p>위와 같이 flex: 1을 부여하게 된다면 왼쪽과 같은 그림이, height:100%를 부여하게 된다면 오른쪽과 같은 그림이 된다.</p>
<img src="https://images.velog.io/images/sky-innerpeace/post/23c24333-1956-4f03-85d1-d2b471bb16ca/image.png" width=100px style="display:inline"/>

<img src="https://images.velog.io/images/sky-innerpeace/post/8e1bfe6a-e14e-41eb-8c8e-2f8266c83d52/image.png" width=100px style="display:inline"/>

<h3 id="📍-flex-관련-스타일-속성">📍 flex 관련 스타일 속성</h3>
<h4 id="실습-화면-6">실습 화면</h4>
<img src="https://images.velog.io/images/sky-innerpeace/post/f6df17b7-bd4c-4c0b-87ec-2427880b6576/image.png" />


<p><strong>1. TopBar.tsx</strong>
화면 상단의 노란색 바. 
flexDirection의 기본값이 수직으로 쌓는 column인데, TopBar에서는 row 적용.</p>
<p><strong>✔ flexDirection 속성</strong>
➡ row, column</p>
<ul>
<li>row<img src="https://images.velog.io/images/sky-innerpeace/post/c50ead1d-2a44-418f-9511-23ae046a1374/image.png" /></li>
<li>column<img src="https://images.velog.io/images/sky-innerpeace/post/48d4fe9b-f1c8-4aa1-9fb0-c494a1dd1964/image.png" />


</li>
</ul>
<ul>
<li>flexDirection 속성 row로 적용<pre><code>import React from &#39;react&#39;;
import {StyleSheet, View, Text, Image} from &#39;react-native&#39;;
import {Colors} from &#39;react-native-paper&#39;;
import Icon from &#39;react-native-vector-icons/MaterialCommunityIcons&#39;;
import * as D from &#39;../data&#39;;
</code></pre></li>
</ul>
<p>const avatarUrl = D.randomAvatarUrl()
const name = D.randomName()</p>
<p>export default function TopBar() {
  return (
    <View style={[styles.view]}>
      &lt;Image style={styles.avatar} source={{uri: avatarUrl}} /&gt;
      <View style={styles.centerView}>
        <Text style={[styles.text]}>{name}</Text>
      </View>
      <Icon name="menu" size={24} color="white" />
    </View>
  );
}</p>
<p>const styles = StyleSheet.create({
  view: {
    flexDirection: &#39;row&#39;,
    alignItems: &#39;center&#39;,
    padding: 5,
    backgroundColor: Colors.amber500,
  },
  text: {fontSize: 20, textAlign: &#39;center&#39;},
  avatar: {width: 40, height: 40, borderRadius: 20},
  centerView: {flex: 1}
});</p>
<pre><code>&lt;br/&gt;

**2. BottomBar.tsx**
하단 네비게이션 바. 
네비게이션 버튼 아이콘의 이름을 icons에 저장한 후 map 함수로 꺼낸다. 그리고 Icon 컴포넌트의 name에 해당 이름을 넣어 매칭시켰다.

**✔ alignItems 속성**
➡ left, center, right, stretch

**✔ justifyContent 속성**
➡ flex-start, center, flex-end, space-around, space-between, space-evenly
</code></pre><p>import React from &#39;react&#39;;
import {StyleSheet, View} from &#39;react-native&#39;;
import {Colors} from &#39;react-native-paper&#39;;
import Icon from &#39;react-native-vector-icons/MaterialCommunityIcons&#39;;</p>
<p>const iconSize = 30, iconColor = &#39;white&#39;;
const icons = [&#39;home&#39;, &#39;table-search&#39;, &#39;face-profile-woman&#39;, &#39;account-settings&#39;];</p>
<p>export default function BottomBar() {
  const children = icons.map((name) =&gt; (
    <Icon key={name} name={name} size={iconSize} color={iconColor} />
  ))
  return <View style={styles.view}>{children}</View>
}</p>
<p>const styles = StyleSheet.create({
  view: {
    flexDirection: &#39;row&#39;,
    alignItems: &#39;center&#39;,
    justifyContent: &#39;space-evenly&#39;,
    padding: 10,
    backgroundColor: Colors.lightBlue500,
  }
})</p>
<pre><code>&lt;br/&gt;

**3. Content.tsx**

**✔ flexWrap 속성**
➡ nowrap, wrap, wrap-reverse

flexWrap 속성값에 따라 아래와 같은 차이가 있다.
* wrap
![](https://images.velog.io/images/sky-innerpeace/post/d9381dab-9fdf-4236-b3f3-307772434cd4/image.png)
* nowrap
![](https://images.velog.io/images/sky-innerpeace/post/1e1299b8-1528-4473-a72e-949c18f278bb/image.png)
* wrap-reverse
상단바까지 자리를 침입한다.
![](https://images.velog.io/images/sky-innerpeace/post/ddc5761a-0aa9-4526-9d27-567996237d15/image.png)

**✔ overflow 속성**
➡ visible, hidden, scroll

특이하게 overflow 속성 중 scroll은 작동하지 않는다. 리액트 네이티브에서 스크롤은 ScrollView나 FlatList 코어 컴포넌트에서만 가능하다.</code></pre><p>import React from &#39;react&#39;;
import {StyleSheet, View, Image} from &#39;react-native&#39;;
import * as D from &#39;../data&#39;;</p>
<p>const avatars = D.makeArray(200).map((notUsed) =&gt; D.randomAvatarUrl())
export default function Content() {
  const children = avatars.map((avatarUrl, index) =&gt; (
    <View key={index.toString()} style={styles.avatarView}>
      &lt;Image style={styles.avatar} source={{uri: avatarUrl}} /&gt;
    </View>
  ))
  return <View style={[styles.view]}>{children}</View>
}</p>
<p>const styles = StyleSheet.create({
  view: {
    flexDirection: &#39;row&#39;,
    flexWrap: &#39;wrap&#39;,
    justifyContent: &#39;center&#39;,
    flex: 1,
    padding: 5,
  },
  text: {fontSize: 20},
  avatarView: {padding: 3},
  avatar: {width: 50, height: 50, borderRadius: 25},
})</p>
<pre><code>&lt;br/&gt;

**4. App.tsx**</code></pre><p>import React from &#39;react&#39;;
import {SafeAreaView, StyleSheet} from &#39;react-native&#39;;
import {Colors} from &#39;react-native-paper&#39;;
import TopBar from &#39;./src/screens/TopBar&#39;;</p>
<p>export default function App() {
  return (
    <SafeAreaView style={[styles.flex]}>
      <TopBar />
      <Content />
      <BottomBar />
    </SafeAreaView>
  );
}</p>
<p>const styles = StyleSheet.create({
  flex: {flex: 1, backgroundColor: Colors.lightBlue100},
});</p>
<pre><code>
### 📍 ScrollView의 contentContainerStyle 속성

#### 실습 화면

&lt;img src=&quot;https://images.velog.io/images/sky-innerpeace/post/a3218775-9a7e-4cb2-9468-4a7e3a70b61f/image.png&quot; /&gt;

**Content.tsx**</code></pre><p>import React from &#39;react&#39;;
import {StyleSheet, View, Image, ScrollView} from &#39;react-native&#39;;
import * as D from &#39;../data&#39;;</p>
<p>const avatars = D.makeArray(200).map((notUsed) =&gt; D.randomAvatarUrl())
export default function Content() {
  const children = avatars.map((avatarUrl, index) =&gt; (
    <View key={index.toString()} style={styles.avatarView}>
      &lt;Image style={styles.avatar} source={{uri: avatarUrl}} /&gt;
    </View>
  ));
  return (
    <ScrollView contentContainerStyle={[styles.view]}>{children}</ScrollView>
  )
}
const styles = StyleSheet.create({
  view: {
    flexDirection: &#39;row&#39;,
    flexWrap: &#39;wrap&#39;,
    justifyContent: &#39;center&#39;,
    padding: 5,
  },
  text: {fontSize: 20},
  avatarView: {padding: 3},
  avatar: {width: 50, height: 50, borderRadius: 25},
})</p>
<pre><code>
**App.tsx**</code></pre><p>import React from &#39;react&#39;;
import {Alert, Platform, SafeAreaView, StyleSheet, View} from &#39;react-native&#39;;
import {Colors} from &#39;react-native-paper&#39;;
import Icon from &#39;react-native-vector-icons/MaterialCommunityIcons&#39;;
import TopBar from &#39;./src/screens/TopBar&#39;;
import Content from &#39;./src/screens/Content&#39;;
import BottomBar from &#39;./src/screens/BottomBar&#39;;</p>
<p>const iconPressed = () =&gt; Alert.alert(&#39;Icon pressed.&#39;)
export default function App() {
  return (
    &lt;&gt;
      <SafeAreaView style={[styles.flex]}>
        <TopBar />
        <Content />
        <BottomBar />
      </SafeAreaView>
      <View style={[styles.absoluteView]}>
        <Icon name="feature" size={50} color="white" onPress={iconPressed} />
      </View>
    &lt;/&gt;
  );
}</p>
<p>const styles = StyleSheet.create({
  flex: {flex: 1, backgroundColor: Colors.lightBlue100},
  absoluteView: {
    backgroundColor: Colors.purple900,
    position: &#39;absolute&#39;,
    right: 30,
    bottom: Platform.select({ios: 100, android: 80}),
    padding: 10,
    borderRadius: 35
  }
});</p>
<pre><code>
## 3-5. 재사용할 수 있는 컴포넌트 만들기

#### FlatList 실습 화면

&lt;img src=&quot;https://images.velog.io/images/sky-innerpeace/post/f2150baf-aa33-40f2-947c-e655fb5d958c/image.png&quot; /&gt;
</code></pre><p>import React from &quot;react&quot;;
import { FlatList, SafeAreaView, StyleSheet, View } from &quot;react-native&quot;;
import { Colors } from &quot;react-native-paper&quot;;
import Person from &#39;./src/copy/Person&#39;
import * as D from &#39;./src/data&#39;;</p>
<p>const people: D.IPerson[] = D.makeArray(10).map(D.createRandomPerson)</p>
<p>export default function App() {
  return (
    <SafeAreaView style={styles.flex}>
      &lt;FlatList data={people}
        renderItem={({item}) =&gt; <Person person={item} />}
        keyExtractor={(item, index) =&gt; item.id}
        ItemSeparatorComponent={() =&gt; <View style={styles.itemSeparator} />} /&gt;
    </SafeAreaView>
  )
}</p>
<p>const styles = StyleSheet.create({
  flex: {flex: 1},
  itemSeparator: {borderWidth: 1, borderColor: Colors.grey500}
})</p>
<pre><code>
#### 📍 moment 패키지 기능 사용하기
* Date 클래스 : 날짜와 시간 관련 기능 처리
* new 키워드로 인스턴스를 생성하여 사용

&lt;br/&gt;

✔ moment 패키지
* Date 클래스가 제공하지 않는 기능을 제공하는 날짜, 시간 처리 패키지
* 특정 포맷으로 날짜, 시간을 깔끔하게 출력
* 과거와 현재의 시간 차이를 알기 쉬운 형태로 출력
* moment-with-locales-es6 패키지를 사용하고 **moment.locale(&#39;ko&#39;)** 명령어를 입력하여 한글 출력 가능

#### 📍 Person 컴포넌트 기본
Person 컴포넌트과 Person에 적용할 스타일 파일을 제작합니다.
**src/copy/Person.tsx**
* typescript을 이용해 변수에 React.FC형을 지정해주었다. 간단한 사용법은 [여기](https://gusrb3164.github.io/web/2021/03/06/typescript-react/)를 참고하자.
</code></pre><p>import moment from &#39;moment&#39;
import React, {FC} from &#39;react&#39;
import {Text, View, Image} from &#39;react-native&#39;
import * as D from &#39;../data&#39;
import {styles} from &#39;./Person.style&#39;</p>
<p>export type PersonProps = {
  person: D.IPerson
}</p>
<p>const Person: FC<PersonProps> = ({person}) =&gt; {
  return (
    <View style={[styles.view]}>
      &lt;Image source={{uri: person.avatar}} style={[styles.avatar]} /&gt;
      <View style={[styles.nameEmailView]}>
        <Text style={[styles.name]}>{person.name}</Text>
        <Text style={[styles.email]}>{person.email}</Text>
      </View>
      <View style={[styles.dateView]}>
        <Text style={[styles.createdDate]}>
          {moment(person.createdDate).startOf(&#39;day&#39;).fromNow()}
        </Text>
      </View>
      <Text style={[styles.text]}>{person.comments}</Text>
      &lt;Image style={[styles.image]} source={{uri: person.image}} /&gt;
      <View style={[styles.countsView]}>
        <Text style={[styles.counts]}>{person.counts.comment}</Text>
        <Text style={[styles.counts]}>{person.counts.retweet}</Text>
        <Text style={[styles.counts]}>{person.counts.heart}</Text>
      </View>
    </View>
  );
}</p>
<p>export default Person</p>
<pre><code>
**src/copy/Person.style.ts**</code></pre><p>import {StyleSheet} from &#39;react-native&#39;;
import {Colors} from &#39;react-native-paper&#39;;</p>
<p>export const styles = StyleSheet.create({
  view: {backgroundColor: Colors.lime100, padding: 5},
  avatar: {width: 50, height: 50, borderRadius: 25},
  nameEmailView: {flexDirection: &#39;row&#39;, alignItems: &#39;center&#39;},
  name: {marginRight: 5, fontSize: 22, fontWeight: &#39;500&#39;},
  email: {},
  dateView: {},
  createdDate: {},
  text: {},
  image: {wdith: &#39;100%&#39;, height: 150},
  countsView: {
    flexDirection: &#39;row&#39;,
    padding: 3,
    justifyContent: &#39;space-around&#39;,
  },
  counts: {},
})</p>
<pre><code>
#### 📍 재사용할 수 있는 컴포넌트 만들기
* 사용자 컴포넌트 예시</code></pre><p><IconText viewStyle={styles.touchableIcon} onPress={onPress}
    name="comment" size={24} color='blue'
    textStyle={styles.iconText} text={person.counts.comment} />`</p>
<pre><code>* React.FC를 활용한 재사용 컴포넌트</code></pre><p>import type {FC, ReactNode} from &#39;react&#39;</p>
<p>type SomeComponentProps = {
    children?: ReactNode
}</p>
<p>export const SomeComponent: FC<SomeComponentProps> = ({children}) =&gt; {
    return <View>{children}</View>
}</p>
<pre><code>
#### 📍 TouchableView 컴포넌트 만들기
</code></pre><p>import React from &#39;react&#39;;
import type {FC, ReactNode, ComponentProps} from &#39;react&#39;;
import {TouchableOpacity, View} from &#39;react-native&#39;;</p>
<p>type TouchableOpacityProps = ComponentProps<typeof TouchableOpacity></p>
<p>export type TouchableViewProps = TouchableOpacityProps &amp; {
  children?: ReactNode;
};</p>
<p>export const TouchableView: FC<TouchableViewProps> = ({children, ...touchableProps}) =&gt; {
  return (
    <TouchableOpacity onPress={...touchableProps}>
      <View>{children}</View>
    </TouchableOpacity>
  )
}</p>
<pre><code>아래의 코드에서 주목할 부분은</code></pre><p>type TouchableOpacityProps = ComponentProps<typeof TouchableOpacity></p>
<pre><code>이 부분이다. 이 부분은 TouchableOpacity의 props를 가져와 TouchableOpacityProps에 담고, 이를 </code></pre><p>export type TouchableViewProps = TouchableOpacityProps &amp; {
  children?: ReactNode;
};</p>
<pre><code>이렇게 교집합 타입을 이용해 children props에 더해준다. 그리고 이렇게 추가한 TouchableOpacityProps는</code></pre><p>export const TouchableView: FC<TouchableViewProps> = ({children, ...touchableProps}) =&gt; {</p>
<pre><code>스프레드 연산자에 의해 사용되는 것을 볼 수 있다.

사실 이 코드는 조금 더 생략할 수 있다. TouchableViewProps는 FC 타입이므로 ReactNode 타입인 **children 속성을 기본적으로 포함**한다. 따라서 굳이 교집합 타입 연산자를 써주지 않고 아래와 같이 TouchableOpacityProps만 추가해도 된다.</code></pre><p>export type TouchableViewProps = TouchableOpacityProps</p>
<pre><code>&lt;br /&gt;

#### 📍 StyleProp 타입
* StyleProp은 react-native 컴포넌트에서 제공하는 타입
* StyleProp&lt;ViewStyle&gt;은 ViewStyle과 의미상 같다. 그런데 StyleProp을 사용하는 이유는 [여기](https://thewavelet.tistory.com/67)를 참고하자.

#### 실습 코드</code></pre><p>import React from &#39;react&#39;;
import type {FC, ComponentProps} from &#39;react&#39;;
import {TouchableOpacity, View} from &#39;react-native&#39;;
import type {StyleProp, ViewStyle} from &#39;react-native&#39;;</p>
<p>type TouchableOpacityProps = ComponentProps<typeof TouchableOpacity></p>
<p>export type TouchableViewProps = TouchableOpacityProps &amp; {
  viewStyle?: StyleProp<ViewStyle>
};</p>
<p>export const TouchableView: FC<TouchableViewProps> = ({children, viewStyle, ...touchableProps}) =&gt; {
  return (
    <TouchableOpacity onPress={...touchableProps}>
      <View style={[viewStyle]}>{children}</View>
    </TouchableOpacity>
  )
}</p>
<pre><code>
#### 📍 Avatar 컴포넌트 만들기
* Avatar 컴포넌트 속성 : 
  uri / size : Avatar의 고유 속성
  viewStyle / onPress : Avatar를 구현하는 데 사용하는 TouchableView의 속성

**src/components/Avatar.tsx**</code></pre><p>import React from &quot;react&quot;;
import type {FC} from &#39;react&#39;;
import {Image} from &#39;react-native&#39;;
import type { StyleProp, ImageStyle } from &quot;react-native&quot;;
import {TouchableView} from &#39;./TouchableView&#39;;
import type { TouchableViewProps } from &quot;./TouchableView&quot;;</p>
<p>export type AvatarProps = TouchableViewProps &amp; {
  uri: string
  size: number
  imageStyle?: StyleProp<ImageStyle>
}
//prettier-ignore
export const Avatar: FC<AvatarPorps> = ({uri, size, imageStyle, ...touchableViewProps}) =&gt; {
  return (
    &lt;TouchableView {...touchableViewProps}&gt;
      &lt;Image source={{uri}}
        style={[imageStyle, {width: size, height: size, borderRadius: size/2}]} /&gt;
    </TouchableView>
  )
}</p>
<pre><code>&lt;br/&gt;

#### 📍 IconText 컴포넌트 만들기
</code></pre><p>import React from &quot;react&quot;;
import type {FC, ComponentProps} from &#39;react&#39;;
import {Text} from &#39;react-native&#39;;
import type { StyleProp, TextStyle } from &quot;react-native&quot;;
import { TouchableView, TouchableViewProps } from &quot;./TouchableView&quot;;
import Icon from &quot;react-native-vector-icons/MaterialCommunityIcons&quot;;</p>
<p>export type IconTextProps = TouchableViewProps &amp;
  ComponentProps<typeof Icon> &amp; {
    text: number | string
    textStyle: StyleProp<TextStyle>
  }</p>
<p>export const IconText: FC<IconTextProps> = ({name, size, color, textStyle, text, ...touchableViewProps}) =&gt; {
    return (
      &lt;TouchableView {...touchableViewProps}&gt;
        <Icon name={name} size={size} color={color} />
        <Text style={textStyle}>{text}</Text>
      </TouchableView>
    )
}</p>
<pre><code>
#### ▶ Text 코어 컴포넌트의 주요 속성
* numberOfLines={숫자}
렌더링하려는 텍스트 줄 수 제한
* ellipsize
head, middle, tail, clip
* textDecorationLine
* textDecorationColor

#### 📍 Person 컴포넌트 스타일 완료

#### 실행 화면

&lt;img src=&quot;https://images.velog.io/images/sky-innerpeace/post/079db7cd-25ac-429e-adde-510f1f5ce4f7/image.png&quot; /&gt;

**Person.tsx**</code></pre><p>import React from &#39;react&#39;
import type {FC} from &#39;react&#39;
import {Text, View, Image, Alert} from &#39;react-native&#39;
import * as D from &#39;../data&#39;
import {styles} from &#39;./Person.style&#39;
import moment from &#39;moment-with-locales-es6&#39;
import {Colors} from &#39;react-native-paper&#39;
import Icon from &#39;react-native-vector-icons/MaterialCommunityIcons&#39;
import {Avatar, IconText} from &#39;../components&#39;</p>
<p>moment.locale(&#39;ko&#39;)</p>
<p>export type PersonProps = {
  person: D.IPerson
}</p>
<p>const avatarPressed = () =&gt; Alert.alert(&#39;avatar pressed.&#39;)
const deletePressed = () =&gt; Alert.alert(&#39;delete pressed.&#39;)
const countIconPressed = (name: string) =&gt; () =&gt; Alert.alert(<code>${name} pressed.</code>)</p>
<p>//prettier-ignore
const Person: FC<PersonProps> = ({person}) =&gt; {
  return (
    <View style={[styles.view]}>
      <View style={[styles.leftView]}>
        <Avatar imageStyle={[styles.avatar]} uri={person.avatar} size={50}
          onPress={avatarPressed} />
      </View>
      <View style={[styles.rightView]}>
        <Text style={[styles.name]}>{person.name}</Text>
        <Text style={[styles.email]}>{person.email}</Text>
      <View style={[styles.dateView]}>
        <Text style={[styles.text]}>
          {moment(person.createdDate).startOf(&#39;day&#39;).fromNow()}
        </Text>
        <Icon name='trash-can-outline' size={26} color={Colors.lightBlue500}
          onPress={deletePressed} />
      </View>
      &lt;Text numberOfLines={3} ellipsizeMode=&quot;tail&quot; style={[styles.text, styles.comments]}&gt;{person.comments}</Text>
      &lt;Image style={[styles.image]} source={{uri: person.image}} /&gt;
      <View style={[styles.countsView]}>
        &lt;IconText viewStyle={[styles.touchableIcon]}
          onPress={countIconPressed(&#39;comment&#39;)}
          name=&quot;comment&quot; size={24} color={Colors.blue500}
          textStyle={[styles.iconText]} text={person.counts.comment} /&gt;
        &lt;IconText viewStyle={[styles.touchableIcon]}
          onPress={countIconPressed(&#39;retweet&#39;)}
          name=&quot;comment&quot; size={24} color={Colors.blue500}
          textStyle={[styles.iconText]} text={person.counts.retweet} /&gt;
        &lt;IconText viewStyle={[styles.touchableIcon]}
          onPress={countIconPressed(&#39;heart&#39;)}
          name=&quot;comment&quot; size={24} color={Colors.blue500}
          textStyle={[styles.iconText]} text={person.counts.heart} /&gt;
      </View>
    </View>
    </View>
  );
}</p>
<p>export default Person</p>
<pre><code>
**Person.style.ts**</code></pre><p>import { StyleSheet } from &quot;react-native&quot;
import { Colors } from &quot;react-native-paper&quot;</p>
<p>//prettier-ignore
export const styles = StyleSheet.create({
  view: {flexDirection: &#39;row&#39;, backgroundColor: Colors.lime100, padding: 5},
  leftView: {padding: 5},
  avatar: {borderColor: Colors.blue500, borderWidth: 2},
  rightView: {flex: 1, padding: 5, marginRight: 10},
  name: {marginRight: 5, fontSize: 22, fontWeight: &#39;500&#39;},
  email: {textDecorationLine: &#39;underline&#39;,
      color: Colors.blue500, textDecorationColor: Colors.blue500},
  dateView: {flexDirection: &#39;row&#39;, justifyContent: &#39;space-between&#39;,
            padding: 3, marginTop: 5},
  text: {fontSize: 16},
  comments: {marginTop: 10, fontSize: 16},
  image: {height: 150, marginTop: 15},
  countsView: {flexDirection: &#39;row&#39;, padding: 3, justifyContent: &#39;space-around&#39;},
  touchableIcon: {flexDirection: &#39;row&#39;, padding: 5, alignItems: &#39;center&#39;},
  iconText: {color: Colors.deepPurple500, marginLeft: 3}
})
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[혼공단 7기]혼공JS 6주차_마지막]]></title>
            <link>https://velog.io/@sky-innerpeace/%ED%98%BC%EA%B3%B5%EB%8B%A8-7%EA%B8%B0%ED%98%BC%EA%B3%B5JS-6%EC%A3%BC%EC%B0%A8</link>
            <guid>https://velog.io/@sky-innerpeace/%ED%98%BC%EA%B3%B5%EB%8B%A8-7%EA%B8%B0%ED%98%BC%EA%B3%B5JS-6%EC%A3%BC%EC%B0%A8</guid>
            <pubDate>Tue, 01 Mar 2022 07:05:50 GMT</pubDate>
            <description><![CDATA[<hr>

<h2 id="혼자-공부하는-자바스크립트-6주차">혼자 공부하는 자바스크립트 6주차</h2>
<h3 id="🎨-기본미션">🎨 기본미션</h3>
<h3 id="p315의-직접-해보는-손코딩을-실행한-후-출력되는-고양이-이미지-캡쳐하기">p.315의 &lt;직접 해보는 손코딩&gt;을 실행한 후 출력되는 고양이 이미지 캡쳐하기.</h3>
<blockquote>
<h4 id="📍-코드">📍 코드</h4>
<img src="https://images.velog.io/images/sky-innerpeace/post/4b24d185-03c6-4d2f-8b78-7b53c7361071/image.png" width="600px"/>

<h4 id="📍-결과화면">📍 결과화면</h4>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/c7bc38ac-afbd-4f09-948c-3c00ea575a72/image.png" alt=""></p>
</blockquote>
<h3 id="🎨-선택미션">🎨 선택미션</h3>
<h3 id="p352-누적-예제를-활용하여-본인의-할-일-목록을-만들어-캡쳐하기">p.352 누적 예제를 활용하여 본인의 할 일 목록을 만들어 캡쳐하기.</h3>
<blockquote>
</blockquote>
<p>📍 <strong>결과화면</strong>
<img src="https://images.velog.io/images/sky-innerpeace/post/80501b0f-43a2-4bbb-b5f1-02afc206f192/image.png" alt=""></p>
<hr>

<p><img src="https://images.velog.io/images/sky-innerpeace/post/33a7c9e9-3890-49dd-b4d6-7f3bc289dbe5/%EB%82%98%EB%8A%94%EC%B2%9C%EC%9E%AC%EB%8B%A4.png" alt="">
이렇게 얼렁뚱땅 또 공부를 마치게 되었군요. 벌써 삼일절이라니, 저저번주에 너무 바빠서 저번주에 같이 내야지~ 했는데 그것조차도 못했네요. 많이 늦긴 했지만 그래도 끝까지 마칠 수 있었던 건 재밌는 혼공족장님과~ 재밌는 책 덕분이었던 것 같습니다. 미처 몰랐던 사소한 것들을 채워나가며 조금 더 완성형에 가까워질 수 있던 것 같네요! 덕분에 일할 때 도움도 됐구요.</p>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/21dc56e9-52d7-4766-8e3c-b101ae7afe33/%EC%82%AC%EA%B3%BC%EB%93%9C%EB%A6%BD%EB%8B%88%EB%8B%A4.png" alt="">
<strong>To. 혼공족장님</strong>
제 글을 기다리셨을 혼공족장님<del>(누가 그럼?)</del>께 심심한 사과의 말씀을 드립니다..
혼공단 미션을 하는 동안 꽤나 즐거웠답니다.. 지금까지는 생존형으로만 자바스크립트를 공부했었는데 이번 기회에 탄탄하게 공부할 수 있는 기회가 생겨서 좋았습니다 오예 그리고 혼공족장님과 하는 스몰톡도 좋았고.. 여고추리반2가 끝나서 지금 시즌1 다시 보면서 글 적고 있네요 껄껄(다시 봐도 존잼)</p>
<p>2월에는 정말 힘들었는데 벌써 3월이네요,, 저도 이제 슬슬 적응하고 일기나 TIL도 조금씩 적을.. 수 있겠죠? 어쨌든! 이 글 보고 계신 여러분 수고 많으셨고 응원합니다 🐣✨</p>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/c379ec1b-770d-4901-ae71-8ada63b2fa88/%ED%9C%98%ED%99%A9%EC%B0%AC%EB%9E%80.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[혼공단 7기]혼공 JS 5주차]]></title>
            <link>https://velog.io/@sky-innerpeace/%ED%98%BC%EA%B3%B5%EB%8B%A8-7%EA%B8%B0%ED%98%BC%EA%B3%B5-JS-5%EC%A3%BC%EC%B0%A8</link>
            <guid>https://velog.io/@sky-innerpeace/%ED%98%BC%EA%B3%B5%EB%8B%A8-7%EA%B8%B0%ED%98%BC%EA%B3%B5-JS-5%EC%A3%BC%EC%B0%A8</guid>
            <pubDate>Tue, 01 Mar 2022 05:04:52 GMT</pubDate>
            <description><![CDATA[<p>잡다한 농담을 남길 시간도 없이 밀린 과제하는 나. 벌써 졸업반인데 이렇게 살아도 되나요?
2주째 회사에 시달려서 딱 끝나고 처음 쉬는 날인 오늘.. 5,6주차를 한 번에 하겠습니다 🍎</p>
<h2 id="혼자-공부하는-자바스크립트-5주차">혼자 공부하는 자바스크립트 5주차</h2>
<h3 id="🎨-나만-보는-개념-정리">🎨 나만 보는 개념 정리</h3>
<p>적을 시간이 없어서 안 적으려고 했는데 간단하게라도 정리해야 할 부분이 있어서..</p>
<h4 id="0-객체와-객체가-아닌-것">0. 객체와 객체가 아닌 것</h4>
<p>자바스크립트 내 자료형들이 모든 것이 객체인 것은 아니다. 이전에 배웠던 <strong>기본 자료형</strong>은 객체가 아니다. 기본 자료형에는 숫자, 문자열, 불이 있고 이들은 속성값을 가진 객체가 아니라 아래와 같이 선언한다. 또한, 객체가 아닌 것에 속성값을 부여하려고 하면 추가되지 않는 것을 알 수 있다. 객체, 속성에 대한 설명은 아래 기본미션에서 정리하였다.</p>
<pre><code>const c = 273
c.sample = 10
c.sample // undefined⭐️⭐️</code></pre><h4 id="1-기본-자료형을-객체로-선언하기">1. 기본 자료형을 객체로 선언하기</h4>
<pre><code>const 객체 = new 객체 자료형 이름()</code></pre><pre><code>new Number(10)
new String(&#39;이렇게 선언하면 됩니다&#39;)
new Boolean(true)</code></pre><h4 id="2-동적으로-객체-속성을-추가하거나-삭제하기">2. 동적으로 객체 속성을 추가하거나 삭제하기</h4>
<pre><code>const student = {
    별명 : &#39;포로&#39;,
    학년 : 3
}

// 동적으로 객체 속성 추가하기
student.학과 = &#39;컴퓨터공학과&#39; 
student[재학중] = false

// 동적으로 객체 속성 삭제하기
delete student.별명 </code></pre><h4 id="3-기본-자료형의-일시적-승급">3. 기본 자료형의 일시적 승급</h4>
<p>문자열은 객체가 아니라고 했는데, 왜 <strong>문자열.length</strong>는 될까? 온점을 붙이면 객체의 속성을 꺼낸다는 뜻인데 문자열은 기본 자료형이라며? 그리고 왜 0번 예시에서는 오류가 나지 않을까? 이 모든 게 기본 자료형의 <strong>일시적 승급</strong> 때문이다. 자바스크립트에서는 기본 자료형 사용 시에 편리함을 제공하기 위해 기본 자료형으로 선언된 변수가 속성과 메소드를 호출하려고 하면 잠시 객체인 척~ 모른 척해주는 것이다. 이걸 기본 자료형의 일시적 승급이라고 한다. 객체가 격이 더 높으니까 승급이라고.</p>
<h4 id="4-객체-모두가-공유하는-프로토타입으로-숫자-메소드-추가하기">4. 객체 모두가 공유하는 프로토타입으로 숫자 메소드 추가하기</h4>
<pre><code>Number.prototype.power = function(n=2) {
    return this.valueOf() ** n
}

const a = 12
console.log(&#39;a.power():&#39;, a.power()) // a.power(): 144
console.log(&#39;a.power(3):&#39;, a.power(3)) // a.power(3): 1728
console.log(&#39;a.power(4):&#39;, a.power(4)) // a.power(4): 20736</code></pre><h3 id="🎨-기본미션">🎨 기본미션</h3>
<h3 id="객체-속성-메소드가-무엇인지-설명하기">객체, 속성, 메소드가 무엇인지 설명하기</h3>
<blockquote>
<p><strong>객체</strong> : 이름(name)과 값(value)으로 구성된 <strong>속성</strong>을 가진 자바스크립트의 기본 데이터 타입
<strong>속성</strong> : 배열 내부의 &#39;요소(element)&#39;와 비슷하게 객체 내부에<strong>key: value</strong> 형태로 객체 내에 있는 값
<strong>메소드</strong> : 자료형이 <strong>함수 자료형</strong>인 객체의 속성</p>
<pre><code>//javascript
const product = {
    // 키: 값으로 이루어진 속성
    제품명 : &#39;로얄 밀크티&#39;,
    브랜드 : &#39;컴포즈 커피&#39;,
    유형 : &#39;논커피&#39;,
    성분 : &#39;얼그레이 홍차, 우유, 설탕, 꿀&#39;,
    가격 : 3800,
    동작 : function(tea) { console.log(`${tea]를 마신다.`) }</code></pre></blockquote>
<pre><code>&gt;```
product[&#39;제품명&#39;] // &#39;로얄 밀크티&#39;
product[&#39;가격&#39;] // 3800</code></pre><p>여기에서 product가 <strong>객체</strong>, (제품명, 브랜드, 유형, 성분, 가격, 동작)이 <strong>속성</strong>, 동작이 <strong>메소드</strong>이다. 참고로 메소드의 함수는 화살표 함수로도 나타낼 수 있지만, 화살표 함수로 선언하게 되면 this로 객체를 가리킬 때 본인이 아니라 window 객체를 가리키게 된다. 보통 우리는 객체 본체의 정보를 얻어내고 싶을 때 this를 사용하므로 화살표 함수가 아닌 function() {} 과 같은 형식으로 선언하는 게 좋다.</p>
<h3 id="🎨-선택미션">🎨 선택미션</h3>
<h3 id="p-288-확인-문제-3번-풀고-풀이-과정-설명하기">p. 288 확인 문제 3번 풀고, 풀이 과정 설명하기</h3>
<blockquote>
<ol start="3">
<li>모질라 문서에서 Math 객체와 관련된 내용을 읽고 사인 90도의 값을 구해보세요. 참고로 사인 90도는 1입니다. 아주 단순하게 생각해서 구현하면 0.893996663005579라는 결과가 나옵니다. 0.893996663005579가 나왔다면 왜 그런지, 그리고 이를 어떻게 해야 제대로 사용할 수 있는지 구글 검색 등을 활용해서 코드를 수정하세요.</li>
</ol>
</blockquote>
<p>📍 <strong>코드</strong></p>
<pre><code>&lt;script&gt;
    const degree = 90
&gt;    
    console.log(Math.sin(90))    // 0.8939966636005579
    console.log(Math.sin(90*Math.PI / 180))    // 1
&gt;
&lt;/script&gt;</code></pre><p><a href="https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;blogId=scyan2011&amp;logNo=221604956473">https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;blogId=scyan2011&amp;logNo=221604956473</a>
위의 링크를 보고 해결했답니다. 모질라 기본 문서 예시에서 파라미터에 라디안 값이 들어가는 걸 보고 변환을 해줘야겠다고는 생각했지만.. 수학은 절레절레.. 오늘의 교훈 : 공식 문서를 잘 읽자!</p>
<hr>

<p>바로 6주차로 <strong>#가보자고</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[혼공단 7기]혼공JS 4주차]]></title>
            <link>https://velog.io/@sky-innerpeace/%ED%98%BC%EA%B3%B5%EB%8B%A8-7%EA%B8%B0-%ED%98%BC%EA%B3%B5JS-4%EC%A3%BC%EC%B0%A8</link>
            <guid>https://velog.io/@sky-innerpeace/%ED%98%BC%EA%B3%B5%EB%8B%A8-7%EA%B8%B0-%ED%98%BC%EA%B3%B5JS-4%EC%A3%BC%EC%B0%A8</guid>
            <pubDate>Sun, 13 Feb 2022 06:11:34 GMT</pubDate>
            <description><![CDATA[<p>이번 주엔 행복한 일이 많았답니다. 근데 대표 이미지 행복한 짤을 넣으려니까 제 짤방에 그런 행복한 건 없더라고요? 그래서 힘든 척 좀 해보겠습니다. </p>
<p>일단 공테기에 왔습니다.
매일매일 공부하고 싶었던 적은 별로 없지만, 이번엔 찐입니다. 아무래도 회사 스트레스인듯.. 회사에서 스트레스 받고 또래들과 하는 사이드 프로젝트로 힐링하고 있답니다. 사회적으로도 비슷한 사람들끼리 끌리는 경향이 더 강하다고 하잖아요?<del>용어까먹음</del> 뭐 그런 거 아니겠어요..?</p>
<p>아 또 슬픈 일.. 여느때와 같이 로또 팡탈했습니다.</p>
<p>그리고 민정언니가 중궈산 빙판에 분개하여 빙판을 내리쳤고 제 마음도 산산조각 났습니다. 그리고 너무 힘들었던 <del>어쩌구저쩌구</del> 시간으로 인해 구슬만한 눈물을 뚝뚝 흘리는데 그만 같이 울어버렸습니다. 갓민정 이제 행복만 했으면 좋겠네요.</p>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/dc846464-d349-4b01-99b3-080d0e510270/KakaoTalk_20220206_213644527.jpg" alt="">
그래도 이번주에 긍정적인 이벤트가 많아서 너무 행복했습니다. 눈 뜨고 코 베이징 희망편 w/쇼트트랙, 예나 음방 1위, 여고추리반, 사랑하는 유튭팀의 복귀... 이 모든 게 금요일 퇴근 직후부터 일어난 일이라면 믿으시겠습니까? (아물론 예나 음방 1위는 목요일이었습니다만)</p>
<p>어쨌든 이번주는 나태하게 별 것 안해놓고 뭐 열심히 한 척 해보겠습니다. </p>
<hr>

<h2 id="혼자-공부하는-자바스크립트-4주차">혼자 공부하는 자바스크립트 4주차</h2>
<h3 id="🎨-나만-보는-개념-정리">🎨 나만 보는 개념 정리</h3>
<h4 id="콜백함수-활용-예시--foreach">콜백함수 활용 예시 : forEach()</h4>
<p>대표적인 콜백함수를 활용하는 함수
배열이 갖고 있는 함수(메소드)로써 단순하게 배열 내부의 요소를 사용해 콜백 함수를 호출함</p>
<h4 id="ⓐ-하나의-매개변수를-활용value">ⓐ 하나의 매개변수를 활용<strong>(value)</strong></h4>
<blockquote>
<pre><code>const array1 = [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;];
</code></pre></blockquote>
<p>arary1.forEach(function (element) {
    console.log(element);
});</p>
<pre><code>&gt;** 화살표 함수를 활용해봅시다!**</code></pre><p>const array1 = [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;];</p>
<blockquote>
</blockquote>
<p>array1.forEach(element =&gt; console.log(element));</p>
<pre><code>
#### ⓑ 3개의 매개변수를 활용**(value, index, array)**
&gt;```
    const numbers = [273, 52, 103, 32, 57];
&gt;
    numbers.forEach((element, index, array) =&gt; console.log(`${index + 1}번째 값 : ${element}`));</code></pre><h4 id="📍-결과화면">📍 결과화면</h4>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/a8a9ed9f-01a1-4784-a09d-3fe9d2825bf6/image.png" alt=""></p>
<p>그외에도 <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/map">map 함수</a>, <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/filter">filter 함수</a> 또한 콜백 함수를 활용하는 함수에 속합니다.</p>
<h3 id="🎨-기본미션">🎨 기본미션</h3>
<h3 id="p202의-윤년을-확인하는-함수-만들기-예제를-실행하여-2022년이-윤년인지-확인하는-결과-인증샷">p.202의 &lt;윤년을 확인하는 함수 만들기&gt; 예제를 실행하여 2022년이 윤년인지 확인하는 결과 인증샷</h3>
<blockquote>
<h4 id="📍-코드">📍 코드</h4>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/6d5f8357-a041-401b-8bc4-b49ea9b78096/image.png" alt=""></p>
</blockquote>
<h4 id="📍-결과화면-1">📍 결과화면</h4>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/b1d20ca8-9409-45a9-aa9b-dcca7d3a879a/image.png" alt=""></p>
<h3 id="🎨-선택미션">🎨 선택미션</h3>
<h3 id="p240-확인-문제-1번-풀고-풀이-과정-설명하기">p.240 확인 문제 1번 풀고, 풀이 과정 설명하기</h3>
<blockquote>
<ol>
<li>filter 함수의 콜백 함수 부분을 채워서 ① 홀수만 추출, ② 100 이하의 수만 추출, ③ 5로 나눈 나머지가 0인 수만 추출해주세요. 그리고 코드의 실행 결과를 적어보세요.</li>
</ol>
</blockquote>
<p>📍 <strong>코드</strong></p>
<pre><code>&lt;script&gt;
    let numbers = [275, 25, 75, 52, 103, 32, 57, 24, 76];
&gt;    
    numbers = numbers.filter((element) =&gt; element % 2 !== 0 &amp;&amp; element &lt;= 100 &amp;&amp; element % 5 === 0);
&gt;
    console.log(numbers);
&lt;/script&gt;</code></pre><p>📍 <strong>결과화면</strong>
<img src="https://images.velog.io/images/sky-innerpeace/post/58bfb958-28f7-4feb-aa8b-df5e09978899/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>①②③의 조건을 <strong>filter</strong> 함수에 넣어서 numbers 배열에서 알맞은 조건의 숫자만 추출했습니다. filter함수는 <strong>비파괴적 함수</strong>이므로 numbers에 필터링한 결과를 다시 넣어주어 콘솔로 출력합니다.</p>
<hr>

<h3 id="💘이번-주-덕질-포인트">💘이번 주 덕질 포인트</h3>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/59f0af91-cb46-4ba9-96c0-442b73683676/KakaoTalk_20220213_144911419_04.jpg" alt=""></p>
<p>갓.민.정. 본인보다 열심히 하는 사람에게는 기꺼이 메달을 내어주겠다는 그녀는 오늘도 메달 사냥에 나섭니다 💘
제 심장은 현재 중궈에 있습니다. 그녀가 훔.쳐갔기 때문.</p>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/85661430-07ff-4630-9421-84739b2957b3/KakaoTalk_20220213_144911419_01.jpg" alt=""></p>
<p>곽윤기님 우리 뽀시래기 선수들을 지켜주세요 감사합니다 유튜브 대성하세요 <del>그럼 이제 그 고개 좀 들어주시겠어요</del></p>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/6d960ee7-3fdf-4b3d-b203-2fe36afcd224/image.png" alt=""></p>
<h3 id="🎊최예나-솔로데뷔-첫-1위🎊">🎊최예나 솔로데뷔 첫 1위🎊</h3>
<h4 id="smileyyena1stwin-smiley1stwin">#SmileyYena1stWin #SMiLEY1stWin</h4>
<p>매주 금요일 퇴근하기 직전이 세상 제일 힘든데, 이번주 격하게 힐링했습니다. 이제 해리포터로 일주일을 마무리하려고요. 그럼 안녕히. <del>공부는 언제 하니</del></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[인턴일기1]]></title>
            <link>https://velog.io/@sky-innerpeace/%EC%9D%B8%ED%84%B4%EC%9D%BC%EA%B8%B01</link>
            <guid>https://velog.io/@sky-innerpeace/%EC%9D%B8%ED%84%B4%EC%9D%BC%EA%B8%B01</guid>
            <pubDate>Mon, 31 Jan 2022 13:35:51 GMT</pubDate>
            <description><![CDATA[<p>오늘은 인턴 8일차<del>4일차에서 이 글을 쓰기 시작한 나</del>
강한 나로 거듭나는 중입니다. 삐그덕삐그덕 대는 중...</p>
<p>어쨌든 나의 사수는 일주일 내내 친절하게 내주신 업무 힌트까지 주셨다고 한다.</p>
<p>내가 잘 몰랐던 리액트 개념이 종종, 아니 많다 보니 다른 사람 코드를 보고 최대한 결을 유지하면서 픽스하는 편인데, 그게 가끔은 독이 될 때가 있다. 오늘도 그걸로 지적을 받았슴다.
좋아요, 나를 의심하지마<del>!</del>아니야 꾸준히 의심하렴~~</p>
<p>나의 개같은 코드를 보고도 웃어주시는 우리 사수님은... 그런 사수를 만난 나의 인복은..</p>
<p>그래도 맥북프로16인치도 너무 사랑스럽고.. 의도치 않은 재택 때문에 그냥 공부하고 있는 것 같고 그래요.</p>
<p>일주일 동안 셀 수 없이 많은 실수를 했지만 일기장에 적고 묻어버렸습니다.
이번 주에 정리할 큰 개념들만 아래에 정리해 보았답니다.
정리+회고+복습이 담겨 있는..</p>
<hr>


<h2 id="1-git-flow">1. Git Flow</h2>
<p>Git에는 버전관리를 위한 여러가지 기능들이 있는데, 그 중에서도 브랜치가 있다.
브랜치는 말 그대로 나무의 나뭇가지들을 생각하면 되는데, 나무의 큰 몸통이 가장 메인이 되는 master branch이고 뻗어나가는 수많은 가지들이 메인에서 파생된 다른 branch이다.</p>
<p>마스터(메인) 브랜치는 하나고 파생 브랜치는 만들고 싶은 만큼 만들 수 있다. 이때 브랜치를 관리하는 것도 꽤나 노력이 필요한 일이다. 브랜치를 열고 작업을 한 뒤에 그 작업이 다 끝나서 실제 release 수준까지 올라오면 마스터에 합치고 브랜치를 닫는다. 근데 브랜치를 여는 목적도 각양각색이고, 정말 빨리 끝내야하는 작업(hotfix)과 그렇지 않은 작업이 있을 수 있다.</p>
<p>그래서 이번 회사에서는 git flow를 활용해 협업을 한다.
<a href="https://danielkummer.github.io/git-flow-cheatsheet/index.ko_KR.html">https://danielkummer.github.io/git-flow-cheatsheet/index.ko_KR.html</a></p>
<p>git을 쓰긴 썼지만 프로젝트를 할 때도 굳이? branch까지 열 상황은 잘 안 나왔다. 프론트, 백 전문가가 각자 파트를 맡는다기보다는 다 같이 하면서 공부하고 배운 적도 있었고, 각자 해도 구현할 기능이 그리 많지 않아 오전에는 프론트가 오후에는 백이 작업을 하면 되는 상황도 있었다.</p>
<p>그런데 이제는 각자 다른 task를 할당하고, 그 task의 목적도 &#39;새로운 기능 구현&#39;으로 통일된 게 아니라 bug fix, improvement 등으로 다양해지기 때문에 branch를 목적에 맞게 나눠 생성할 수 있는 Git Flow가 필요해진 것이다.</p>
<hr>

<h2 id="2-애자일-프로세스">2. 애자일 프로세스</h2>
<p>평소에도 난 굉장히 모순적인 사람이다. 안정적인 것을 좋아하지만 그렇다고 변화를 싫어하지도 않고, 장기 계획을 그리 선호하지는 않지만 체계적인 건 또 좋아한다. 결국 무엇이든 중간, 반반을 좋아하는 성격 탓에 모순적인 것처럼 보이는 것 같다. 소프트웨어공학 수업을 들을 때도  Waterfall 기반의 RUP(Rational Unified Process)도 강력해보이고, Agile도 쿨해보였다. 실제로 회사에서는 Agile 개발 방법을 채택하고 있었다.</p>
<p>waterfall의 일차적인 실패의 원인이기도 했던 &quot;무슨 일이 있어도 단계를 다시 거슬러 올라가지 않는 특성&quot;을 지양하는 <strong>Agile</strong>은 그야말로 &quot;놓치면 다음 번에&quot; 마인드라고 생각하면 쉽다. <del>마치 내 인생</del>  </p>
<p>이쯤에서 waterfall과 agile이 뭔지 궁금해할 분들을 위해.. 그 전에 소프트웨어 공학이 무엇인지에 대한 이해가 선행되어야 한다.</p>
<h3 id="⭐-소프트웨어-공학이란">⭐ 소프트웨어 공학이란?</h3>
<p>가르쳐주신 교수님의 말을 빌리자면.. (아직도 외우고 있다)</p>
<blockquote>
<p>Software engineering is concerned with theories, methods, and tools for professional and cost-effective software development.</p>
</blockquote>
<p>특별한 말은 아니고, 소프트웨어를 professional하고 cost-effective하게 개발할 수 있도록 하는 다양한 이론, 기법, 도구들을 의미한다고 보면 된다.</p>
<p>그럼 왜? 왜  소프트웨어 공학을 알고 적용시켜야 할까?</p>
<p>소프트웨어 공학은 주로 여러 사람이 큰 시스템을 개발할 때 적용된다. 그리고 <strong>유지보수</strong>가 필요할 때도.</p>
<p>그래서 왜? 내가 이해하기로는, 프로젝트를 실패하지 않기 위해서에 가깝다.
대체로 <strong>기법</strong>이라는 것은, &#39;이런 형식을 따르면 최소 이 정도의 결과물은 나옵니다&#39;라고 보장해주는 것과 비슷하다.
그렇기 때문에 다양한 기업에서 다양한 프로젝트를 실패했던 과거에, 더 이상 실패하지 않기 위해 waterfall이라는 소프트웨어공학 모델(기법)이 개발되었다.</p>
<h3 id="⭐-waterfall-model이란">⭐ Waterfall model이란?</h3>
<p>1960년대, The Software Crisis가 발생하면서 많은 프로젝트들이 실패했다. 한 사람이 개발하던 시스템을 여러 사람이 개발하려고 했던 것이다. 나는 그 당시를 &#39;협업&#39;을 처음 하는 사람들만 잔뜩 모여 본인의 퍼포먼스를 뽐낼 줄만 알던 시절이라고 이해했다. <del>가끔은 이런 상상력이 필요하다</del></p>
<p>이 software crisis를 타개하기 위해 Software Engineering(소프트웨어공학)의 개념이 등장했고, 가장 처음 나온 모델이 바로 Waterfall model이다.</p>
<p><del>교수님이 Waterfall은 꼭 영어로 말하라고 했다. 폭포수 모델은 멋이 떨어지니</del></p>
<p>폭포가 중력에 따라 위에서 아래로만 흐르듯이, 소프트웨어를 개발할 때도 단계 별로, 순서대로 진행하라는 뜻이다. 여기에서 중점적인 것은 <strong>&#39;역류하지 말 것&#39;</strong></p>
<p>주로 다섯 단계로 구분한다.</p>
<blockquote>
<p><strong>Waterfall Model Process</strong></p>
</blockquote>
<ol>
<li>Requirements definition</li>
<li>System and software design</li>
<li>Implementation and unit testing</li>
<li>Integration and system testing</li>
<li>Operation and maintenance</li>
</ol>
<p>design, implementation을 합쳐 네 단계로 합쳐 구분하기도 한다.</p>
<p>어찌 됐든 이 순서대로 거슬러 올라가지 않고 개발하면 실패할 확률을 줄여준다는 게 Waterfall 모델의 key point였다. 실제로 꽤 오래 의미 있게 활용되었고, 지금까지도 다양한 모델과 기법의 근간이 되고 있다. </p>
<p>그럼 왜 실패했냐? 가장 중요한 건 거슬러 올라갈 수 없다는 점이다. 개발 초기에 모든 기능을 생각해내고 이를 구체화시키는 작업을 하기에는 무리가 있다. 중간에 기능을 추가하게 될 수도 있고, 삭제하고 싶을 때도 있다. 하지만 초기 Waterfall에서는 절대 불가능한 일이었다. 결국 여러가지 변형을 통해 이를 개선하였지만, 그럼에도 변하지 않는 것이 하나 있었다.</p>
<p>명세 작성 등의 문서 작업. SRS, SDS, AD ... 정말 많다. 과연 이게 몇장으로 정리될까? SRS 기본 틀만 봐도 만만치 않음을 알 수 있다. 특히나 개발을 해봐야 채울 수 있는 부분들이 종종 있는데(여러가지 이유로) Waterfall에서는 그런 것도 용납하지 않거니와, 그때그때 적어야 할 문서를 완벽하게 작성해놔야 한다.
<img src="https://images.velog.io/images/sky-innerpeace/post/74fcc8bb-5dd6-4001-934f-186e696ca58e/%EB%84%88%EB%A5%BC%EC%A3%BD%EC%9D%B4%EA%B2%A0%EB%8B%A4.jpg" alt="">
문서 작업에 질려버린 사람들이 만들어낸 것. 그것이 바로 Agile이다.</p>
<p>Agile은 변화를 두려워하지 않다. 뭐든지 fix하고 보는 Waterfall은 절대 <strong>No변화</strong>를 외치지만 Agile은 항상 변화에 대비한다. Agile에 대해서는 곧 자세하게 정리할 예정이라서 짧게 스탠스 정도만 소개하고 넘어가겠다. </p>
<h3 id="⭐-agile-기법이란">⭐ Agile 기법이란?</h3>
<p>Agile은 많아도 너무 많은 형식적인 문서 작업에 지쳐버린 사람들 사이에서 1980년대에 등장해 1990년대까지 유행하던 기법이다. Agile은 비슷한 목적과 배경을 지닌 광범위한 기법들을 아울러 부르는 일종의 umbrella term이라, model로 분류하지 않고 method로 분류한다.</p>
<p>페어 프로그래밍, 리팩토링, TFD 같은 개념들이 전부 Agile 개발 테크닉 중 하나다. 
Agile 프로젝트 관리 기법에도 여러가지가 있는데, 대표적으로 스크럼과 스프린트를 떠올릴 수 있다.</p>
<p>어쨌든 스타트업 특성 상 사람이 적어 체계를 잡기도 모호하고, 프로젝트 초기 단계에서는 기능 요구사항을 fix하기도 쉽지 않다. 변화에 빠르게 대처하고 적응해야 하는 생태계이기 때문에 스타트업에서는 Agile 기반의 개발을 선호하지 않을까 싶기는 했다.</p>
<p><del>자 이제 본론</del></p>
<p>어쨌든, 회사에 들어가 난생 처음 <strong>스프린트</strong>와 <strong>스크럼</strong>을 겪어봤다. 단기 계획과 잦은 회고는 체계를 정립하는 데에도 도움이 되는 것 같다는 <del>감상평</del></p>
<p>내 상상과 조금 다른 부분도 있었다. 스크럼에서는 본인이 업무한 내용을 자세하게 공유하는데, 전체 스크럼에서는 모두가 이해할 수 있는 선에서만 공유를 했다. 물론 개발자들끼리 하는 스크럼에서 다양한 이야기를 추가로 나눠서 커뮤니케이션에 무리는 없었지만 생각보다 개발자와 비개발자 간의 간극이 느껴지는 건 기분 탓만은 아닌 것 같았다.</p>
<p>그래도 기획, 디자인 쪽 분들도 이해하려고 많이 노력하시는 것 같았다. 아무래도 업무나 분야 별로 같은 단어라도 세부적인 뜻이 다를 수 있어 발생하는 커뮤니케이션 상 오류도 대부분 문제가 발생하기 전에 스크럼이나 회고에서 정확히 짚고 넘어가서 좋았던 것 같다.</p>
<p>학생들끼리 프로젝트를 해서 그런지 원래 다 그런지는 몰라도, 프로젝트 진행 중에 소통의 부재로 인한 문제들이 가끔 발생했다. 이럴 때 스크럼이 있었다면 좋았을텐데 싶었다. 많이 배우고 체화하여 적용할 예정이다. </p>
<p>이외의 툴들은 공개해도 될지 모든 구조를 공개하기에는 대외비가 아닐지.. 걱정되어 이쯤하겠다.
<del>비밀 유지 서약서 같은 건 쓴 적 없다</del></p>
<hr>

<p>이외에 코딩하면서 찾아보고 배웠던 것들이 은근 많아서 어떻게 정리해야 할 지 고민이다.
어떻게 하다보면 될 것 같다. 내일의 내가.</p>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/8115f866-837e-49ac-a1df-863930904690/KakaoTalk_20220131_005158740_07.jpg" alt=""></p>
<p>저도 이만 날아가보도록 하겠습니다. 그럼 20000. 👊</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[혼공단 7기]혼공JS 3주차]]></title>
            <link>https://velog.io/@sky-innerpeace/%ED%98%BC%EA%B3%B5%EB%8B%A8-7%EA%B8%B0%ED%98%BC%EA%B3%B5JS-3%EC%A3%BC%EC%B0%A8</link>
            <guid>https://velog.io/@sky-innerpeace/%ED%98%BC%EA%B3%B5%EB%8B%A8-7%EA%B8%B0%ED%98%BC%EA%B3%B5JS-3%EC%A3%BC%EC%B0%A8</guid>
            <pubDate>Sun, 30 Jan 2022 16:28:01 GMT</pubDate>
            <description><![CDATA[<p>이번 주는 시간이 없어서 개념 정리를 패스합니다<del>(평소에도 열심히 한 적 없잖아!)</del>
짧은 인턴 일기 : 매일 하루 단위로 멘탈이 강력해지고 철면피를 까는 중.
Question man이 될 수 있는 날은 얼마 남지 않았다는 마음으로 가끔 기초적인 질문도 함
<del>(놀랍게도 인터넷에 나온 방법을 다 써봤지만 해결하지 못한.. css의 늪)</del></p>
<img src="https://images.velog.io/images/sky-innerpeace/post/eef7bd89-3b2d-4dba-8830-38a7801173cb/image.png" width="500px"  style="display: block; margin: 0px auto;" />

<p>css가 js보다 어렵기만 한 나... 이 시대의 공부 편식쟁이랍니다?
이제 강아지풀 뜯어 먹는 소리 집어치우고 본론으로 gabozago.</p>
<h1 id="혼자-공부하는-자바스크립트-3주차">혼자 공부하는 자바스크립트 3주차</h1>
<h2 id="🎨-기본미션">🎨 기본미션</h2>
<h3 id="비파괴적-처리와-파괴적-처리의-의미와-장단점-설명하기">비파괴적 처리와 파괴적 처리의 의미와 장단점 설명하기</h3>
<h4 id="📍-파괴적-처리-vs-비파괴적-처리">📍 파괴적 처리 vs 비파괴적 처리</h4>
<p>파괴적 처리와 비파괴적 처리는 처리 후 원본의 상태가 변화하는지, 유지되는지에 따라 구분합니다.</p>
<ul>
<li>비파괴적 처리 : 처리 후에 원본 내용이 변경되지 않음<ul>
<li>split, map(<strong>array.map((x)⇒x*x)</strong>)</li>
</ul>
</li>
<li>파괴적 처리 : 처리 후에 원본 내용이 변경됨<ul>
<li>push, trim(여백제거)</li>
</ul>
</li>
</ul>
<h4 id="⭐-비파괴적-처리">⭐ 비파괴적 처리</h4>
<pre><code class="language-jsx">const a = &#39;안녕&#39;
const b = &#39;하세요&#39;

const c = a+b
c // &quot;안녕하세요&quot;
a // &quot;안녕&quot;
b // &quot;하세요&quot;</code></pre>
<h4 id="⭐-파괴적-처리">⭐ 파괴적 처리</h4>
<pre><code class="language-jsx">const array = [&#39;사과&#39;, &#39;배&#39;, &#39;바나나&#39;]
array.push(&#39;귤&#39;)
array // [&#39;사과&#39;, &#39;배&#39;, &#39;바나나&#39;, &#39;귤&#39;]</code></pre>
<p>과거에는 컴퓨터 메모리가 많이 부족했는데, 그러다보니 배열과 같은 용량이 커질 수 있는 자료는 <strong>파괴적 처리</strong>를 이용해서 원본을 변경하는 방식을 채택했습니다. 지금은 자료 보호를 위해서 <strong>비파괴적 처리</strong>를 하는 것이 좋다네요. <del>아직도 플로피디스크 같은 구데기를 쓰는 일ㅂ...읍읍</del></p>
<img src="https://images.velog.io/images/sky-innerpeace/post/2c162712-4359-4a76-93a3-95008fc2223b/image.png" href="https://www.wikitree.co.kr/articles/661707" width="700px"/>

<p>출처 : <a href="https://www.wikitree.co.kr/articles/661707">은행 기기에 &#39;플로피디스크&#39; 꽂는 이 나라, 뜻밖에도 옆나라 일본이다</a></p>
<h2 id="🎨-선택미션">🎨 선택미션</h2>
<h3 id="p173-확인-문제-3번-문제-풀고-풀이-과정-설명하기">p.173 확인 문제 3번 문제 풀고, 풀이 과정 설명하기</h3>
<blockquote>
<ol start="3">
<li>다음 표시된 함수들이 파괴적 처리를 하는지 비파괴적 처리를 하는지 구분해 맞는 것에 O 표시하세요.<img src="https://images.velog.io/images/sky-innerpeace/post/76a6a9a7-3a39-46ce-8411-e0f83ee988c5/image.png" width="800px"/>
갖고 있던 배열들을 조작한 뒤 다시 출력했을 때 처음과 같은 결과가 나오면 **비파괴적 처리**, 다른 결과가 나오면 **파괴적 처리**라고 볼 수 있습니다. 1,3번은 두번째 실행문에서 새로운 결과가 출력되었지만, 저 결과는 따로 저장하지 않는 한 사라져버리는 값입니다. 그래서 보통 split, map 함수를 사용할 땐 새로운 변수에 결과값을 할당하죠. 
이와는 반대로 2,4번은 두번쨰 실행문이 포함된 후로는 선언 당시 가졌던 배열의 값을 불러올 수 없으므로 원본이 파괴되었다고 볼 수 있습니다.
</li>
</ol>
</blockquote>
<hr>

<p>엊그제 벨로그 구경 다니다가 본인이 구독한 뉴스레터 추천해주는 글을 보고 나도 뉴스레터를 3개나 더 적립했다(이제 5개 야호!)
읽는 건 은근 많은데 곱씹는 시간이 좀 부족한 것 같다. 아무래도 친구랑 하~루종일 넷상에서 붙어있다보니 혼자만의 시간이 부족한 탓인 것 같은데.. 하루 회고를 열심히 해야겠어.</p>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/7c40553f-e39b-441d-98df-cce442c40449/image.png" alt="">
(현생과 인생을 사는 나)</p>
<hr>

<blockquote>
<h3 id="내-뉴스레터-구독-목록">내 뉴스레터 구독 목록</h3>
<p><strong>1. 뉴닉</strong> 
사회 이슈, 경제, 문화 등등 다양한 이야기를 전해줘서 개발만 할 줄 아는 개발자 아니고 똑똑이 개발자가 될 수 있도록 해줌! 그야말로 융합형 인재 !
구독 링크 : <a href="https://newneek.co/?utm_medium=email&amp;utm_source=newneek_newsletter&amp;utm_campaign=not_yet_subscribed&amp;utm_content=footer_subscription_link">https://newneek.co/?utm_medium=email&amp;utm_source=newneek_newsletter&amp;utm_campaign=not_yet_subscribed&amp;utm_content=footer_subscription_link</a></p>
<p><strong>2. Trend A Word</strong>
  그래그래.. 내가 또 한 유머하지. 타고나기도 했지만.. 노력형 인재에 좀 더 가깝달까..? <del>찡긋.</del> 사실 블로그 쓰기 시작하면서 여러가지 짤들이 필요했는데 여기에서 짤줍도 많이 할 수 있고, 나 같은 할미도 세상의 트렌드를 잘 알 수 있도록 소개해줘서 좋음! 아는 스타트업 대표님 피드에서 보고 바로 구독했었음
링크 : <a href="https://maily.so/trendaword">https://maily.so/trendaword</a></p>
</blockquote>
<p><strong>3. 해킹짹쨱</strong>
2학년 여름방학 때 잠깐 보안 전문가 양성 교육을 들은 적이 있는데 꽤나 흥미로웠거든. 근데 그쪽으로 진로를 정한 것도 아닌데 막 찾아보기에는 딱히 우선순위가 높지도 않았고. 그러다가 이 뉴스레터를 알게 됐는데 굉장히 흥미롭더라고. 아직 구독하고 한 번도 제대로 안 왔는데 지난 뉴스레터를 다 읽어버림 ㅋㅋㅎ
링크 : <a href="https://page.stibee.com/subscriptions/130678">https://page.stibee.com/subscriptions/130678</a></p>
<p>나머지 두 개는 아직 웰컴 메일만 오고 한 번도 받아본 적이 없어서 후기와 함께 다음에 소개하도록 하겠습니다. 😉</p>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/d49651b1-2028-4daa-b570-80196b4a97ea/image.png" alt="">
이제 여고추리반 정주행하면서 잘래.. 내일은 인턴일기와 css 뽀시래기 탈출 대작전으로 찾아뵙겠습니다(실력이 부족하면 자체 초과근무를 해야지..<del>라고 친구한테 말했더니 넌 대학원 가면 교수님이 예뻐할 거니까 대학원이나 가라며...</del>)</p>
<p>그럼 다들 즐거운 설연휴~</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[혼공단 7기]혼공JS 2주차(Feat.Just smile away)]]></title>
            <link>https://velog.io/@sky-innerpeace/%ED%98%BC%EA%B3%B5%EB%8B%A8-7%EA%B8%B0-%ED%98%BC%EA%B3%B5JS-2%EC%A3%BC%EC%B0%A8</link>
            <guid>https://velog.io/@sky-innerpeace/%ED%98%BC%EA%B3%B5%EB%8B%A8-7%EA%B8%B0-%ED%98%BC%EA%B3%B5JS-2%EC%A3%BC%EC%B0%A8</guid>
            <pubDate>Sat, 22 Jan 2022 11:06:27 GMT</pubDate>
            <description><![CDATA[<p>예, 저 다음주부터 출근합니다. 인턴이 되었습니다. 무슨 인턴이냐고요?
<del>리액트개발자요</del> &lt;- ? 너 지금 JS 공부하잖아 뭔 개솔
음음 아무래도 저의 성장 가능성을 높게 평가하신 거겠죠<del>?^^
사실 전 JS도 세번째 보고 있는 거고.. 리액트도 이미 해본 적이 있답니다 껄껄 ~</del>네다음중고~~
그래서 이 얘기를 왜 하냐고요? 면접 준비한다고 이 책을 하루만에 다 봤답니다
꽤 좋은 책임을 체감하고 여기저기 추천하고 있습니다 🤸🏼‍♀️</p>
<hr>

<h1 id="혼자-공부하는-자바스크립트-2주차">혼자 공부하는 자바스크립트 2주차</h1>
<h2 id="🎨-나만-보는-개념-정리">🎨 나만 보는 개념 정리</h2>
<p><del>이번주도 대강 정리해보는 내가 모르....진 않지만 그래도 한번 더 정리해보는 개념</del></p>
<h3 id="1-조건-분기">1. 조건 분기</h3>
<p><strong>조건 분기</strong> : 코드가 실행되는 흐름을 변경하는 것
<strong>ex_</strong> 
조건문을 사용하여 조건에 해당하면 실행하고, 아니면 실행을 안하게 할 수 있는 것처럼. 써있는 모든 코드를 다~~ 실행하는 게 아니고! 상황에 따라 실행되도록 하는 것.</p>
<h3 id="2-조건문">2. 조건문</h3>
<p>그럼 어떻게 조건을 넣지❓
예예 우리 <strong>조건</strong>에 대해 먼저 생각해봅시다.
대출 조건, 취업 조건.. 우리는 수많은 조건을 마주하면 살아가고 있죠? 월 200이상 벌면 대출을 받을 수 있고~ 월 500 이상 벌면<del>(i want)</del> 대출 금리 우대 혜택을 받을 수 있고! 이런 조건들이 참 많죠. &gt;조건&lt;은 그런 거랍니다. 내가 뭘 하면! 뭐라면! 이런 것들.</p>
<p>보통 코딩할 땐 이런 식으로 나타냅니다.</p>
<p><strong>if(x&lt;100)</strong>
변수 x에 100보다 작은 값이 들어있다면</p>
<pre><code>if(x&lt;100) {
 console.log(&quot;100보다 작은 수입니다.&quot;)
} else if(x&lt;200) {
 console.log(&quot;100과 200 사이의 수입니다.&quot;)
} else {
 console.log(&quot;200과 같거나 큰 수입니다.&quot;)
}</code></pre><p>이런 식으로 나온다는 뜻이죠. else if는 if문에 해당되지 않을 때 검사하고, else는 앞의 모든 if문에 해당하지 않는 상황일 때 실행됩니다.</p>
<p>이런 조건을 만들기 위해서 우리는 비교 연산자와 논리 연산자를 저 괄호 안에 넣고, 결과는 꼭 &#39;불 자료형&#39;으로 나와야 합니다. (간혹 숫자로 나오는 경우가 있는데, 컴퓨터에서는 0을 false, 1을 true로 취급하죠)</p>
<hr>

<h2 id="🎨-기본미션">🎨 기본미션</h2>
<h3 id="p139-확인문제">p.139 확인문제</h3>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/b700a0e9-9cad-4bfa-83ae-b7cdc89cabb5/image.png" alt="">
C언어 기본 미션하고 헷갈려서 하마터면 모든 확인 문제를 다 풀어서 낼 뻔 했습니다. 아까우니까 아래 문제는 지우지 않고 남겨두기..</p>
<blockquote>
<ol>
<li>다음 예제 중에서 &#39;참입니다&#39;를 출력하는 것은 몇번일까요?
<img src="https://images.velog.io/images/sky-innerpeace/post/72f30ada-9164-4e9d-ac50-d06af57cc222/image.png" alt="">
답 : 3번
if문 안의 조건들을 보면 x&gt;4로 동일하고, x값만 달라지는데 4보다 큰 값은 3번의 10뿐이니까요!</li>
</ol>
</blockquote>
<blockquote>
<ol start="2">
<li>사용자로부터 숫자 2개를 입력받아 첫 번째 입력받은 숫자가 큰지, 두 번째 입력받은 숫자가 큰지를 구하는 프로그램을 다음 빈칸을 채워 완성해보시오. 
<img src="https://images.velog.io/images/sky-innerpeace/post/13480003-d169-4d07-b5f2-7a97129f7a7f/image.png" alt=""></li>
</ol>
</blockquote>
<h2 id="이게-진짜-문제입니다">이게 진.짜. 문제.입니.다.</h2>
<h3 id="p139-확인-문제-3번-문제-풀고-완전한-코드-만들어-비주얼-스튜디오-코드에서-실행-결과-인증샷">p.139 확인 문제 3번 문제 풀고 완전한 코드 만들어 비주얼 스튜디오 코드에서 실행 결과 인증샷</h3>
<blockquote>
<ol start="3">
<li>중첩 조건문은 2장에서 배운 논리 연산자를 적용해 하나의 if 조건문으로 만들 수 있습니다. 빈칸에 어떤 논리 연산자가 들어가야 할까요?</li>
</ol>
<p><strong>기존 코드</strong>
<img src="https://images.velog.io/images/sky-innerpeace/post/56de4fd2-213a-4ea3-94a3-ab3bef6c03ea/image.png" alt="">
<strong>수정한 코드</strong>
<img src="https://images.velog.io/images/sky-innerpeace/post/99de2f7b-46f5-42ef-a047-3800fc280e39/image.png" alt="">
<strong>결과</strong>
<img src="https://images.velog.io/images/sky-innerpeace/post/881b3e54-dc0d-4d2a-9666-0362fcf5cbbd/image.png" alt="">
어라.. 제가 며칠 전에 듀얼 모니터를 샀는데 캡처화면으로 캡처하면 이런 식으로... 뿌우우우우우옇게 뜨네요? 디스코드 화면공유하면 진짜 가관임.. 4K 샘송 모니터의 왕관의 무게를 견뎌라 뭐 그런 건가?</p>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/2e9be3a1-c641-445d-986c-dc380f0af00a/image.png" alt="">
왼쪽은 찐 모니터 오른쪽은 화면 공유한 왼쪽 모니터를 띄우고 있는 서브모니터.. 어쨌든 <strong>아래가 진짜 결과 !!</strong>
<img src="https://images.velog.io/images/sky-innerpeace/post/00c35b01-162b-45e5-938d-c8e53bb8c932/image.png" alt=""></p>
</blockquote>
<p>실제로 코딩하다보면 &amp;&amp; || &amp;&amp; || 온갖 연산자를 if문 안에 한 번에 넣을 때가 있는데.. 정신을 놓지 말아야 합니다.
정신을 놓았다가는 Unit Testing 때 에러를 신나게 뿜어낼 수 있어요.</p>
<hr>

<h2 id="🎨-선택미션">🎨 선택미션</h2>
<h3 id="p152의-태어난-연도를-입력받아-띠-출력하기-예제-실행하여-본인의-띠-출력한-화면-캡쳐하기">p.152의 &lt;태어난 연도를 입력받아 띠 출력하기&gt; 예제 실행하여 본인의 띠 출력한 화면 캡쳐하기</h3>
<blockquote>
<p><strong>코드</strong>
<img src="https://images.velog.io/images/sky-innerpeace/post/81131188-a79e-41e0-a231-dae9c57cf750/image.png" alt="">
<strong>실행 결과</strong>
<img src="https://images.velog.io/images/sky-innerpeace/post/79e17b9c-2ad9-4b42-a577-2bf8bd64021e/image.png" alt="">
이거 쓰면서 저는 1학년 때 열심히 c언어로 조건문 배우던 때가 생각났답니다.. 더 좋은 방법을 아래에 소개해드릴게요 <del>책에나옴</del></p>
</blockquote>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/05d55753-1154-43bb-bffb-a7215db9a73f/image.png" alt=""></p>
<blockquote>
<p>split(문자)는 문자를 기준으로 문자열을 잘라서 배열로 만드는 거랍니다<del>! 야호</del>!</p>
</blockquote>
<hr>

<p><img src="https://images.velog.io/images/sky-innerpeace/post/0f1505dc-2ece-435c-b548-4092cb0f5751/image.png" alt="">
<em>최예나 비비 사랑해 !!</em>
YENA(최예나) - &#39;SMILEY&#39; 많이 사랑해주세요 💕</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹개발자되기 1일차(React.js, Node.js로 풀스택 개발하기)]]></title>
            <link>https://velog.io/@sky-innerpeace/%EC%9B%B9%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%90%98%EA%B8%B0-1%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@sky-innerpeace/%EC%9B%B9%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%90%98%EA%B8%B0-1%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Sun, 16 Jan 2022 13:09:50 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/sky-innerpeace/post/6c89f393-e628-479b-a12a-e3f06d1e2822/%ED%94%BC%EA%B3%A42.png" alt=""></p>
<h3 id="🤸-오늘도-졸린-하루-🤸">🤸 오늘도 졸린 하루 🤸</h3>
<hr>

<h3 id="리액트도-배웠고-노드도-배웠는데">리액트도 배웠고 노드도 배웠는데..</h3>
<p>아 물론 &#39;다 배운 것&#39;은 아니고, 대충 아아아아주 간단한 버전의 CRUD 정도는 익혔다고 생각했기에(중간에 재미없다고 뛰어넘은 부분 有) 일단 몸을 던져보겠다 선언.</p>
<p>인프런 강의를 결제했다. <del>책에도 비슷한 거 있잖아</del></p>
<p>좀 아까 경제 공부한다고 유튜브 강의 열심히 봤는데 벌써 교훈을 잊음. 돈은 버는 것보다 <del>쓰는 게 쉽다</del> 더 많이 벌자....</p>
<h4 id="그래-이제-다-놀았니-공부하자">그래 이제 다 놀았니? 공부하자</h4>
<h3 id="1-vs-code에서-rfce-단축키가-안-먹어요">1. VS Code에서 rfce 단축키가 안 먹어요</h3>
<h4 id="❓-rfce가-뭔데-그래">❓ <strong>rfce</strong>가 뭔데 그래</h4>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/0d395999-0cca-45ff-9161-3e85a4e87594/image.png" alt="">
예, 별 건 아니고 이런 단축 키워드입니다.
근데 저는 저게 안 뜨는 거예요!
<img src="https://images.velog.io/images/sky-innerpeace/post/ec29ece2-ab61-45a6-aa3e-34185777acf8/image.png" alt="">
물론 이 정도만 적어주면 되지만... 원인을 파악하는 것이 공학도의 자질인 것을.</p>
<blockquote>
<p>검색하자마자 보란듯이 나온 답
나만 몰랐던 <strong>extension</strong>
<a href="https://stackoverflow.com/questions/66118055/built-in-snippet-not-working-in-vs-code-for-react">https://stackoverflow.com/questions/66118055/built-in-snippet-not-working-in-vs-code-for-react</a></p>
</blockquote>
<h3 id="2-ant-design">2. Ant Design</h3>
<p>리액트에서 사용하는 부트스트랩 같은 느낌이 물씬 나는 멋쟁이 라이브러리를 활용했어요 바로바로 <strong>Ant Design</strong></p>
<p><a href="https://ant.design/docs/react/introduce">https://ant.design/docs/react/introduce</a></p>
<p>간단하게 아래의 명령어로 불러와서 사용하시면 된다죠</p>
<pre><code>npm install antd</code></pre><h3 id="3-컴포넌트-이름에-오타가-나면">3. 컴포넌트 이름에 오타가 나면</h3>
<blockquote>
<p>Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it&#39;s defined in, or you might have mixed up default and named imports.</p>
</blockquote>
<p>antd의 TextArea를 사용해야 했는데, Textarea라고 적은 것이 화근이었다.</p>
<hr>

<p>작고 사소하지만 다시 봐도 기억 안 날 것들을 적어봤습니다
생산적으로 살아야겠다고 다짐하는 ... 어제부터!
짤을 열심히 줍줍하고 있는데 이런 짤을 봤다죠</p>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/53375ea9-d550-4429-9b10-5d5cf872f573/%ED%9C%98%ED%99%A9%EC%B0%AC%EB%9E%80.jpg" alt=""></p>
<p>개발천재들 사이에서 저 같은 &#39;E&#39;는 살아남기 힘들겠지만 그래도 올해는 빛날 거라고 믿으며 내일도,, 자도자도 졸린 삶을 살아봅시다 뿅</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[혼공단 7기] 혼공JS 1주차(피곤해도 할 건 해야지)]]></title>
            <link>https://velog.io/@sky-innerpeace/%ED%98%BC%EA%B3%B5%EB%8B%A8-7%EA%B8%B0-%ED%98%BC%EA%B3%B5JS-1%EC%A3%BC%EC%B0%A8</link>
            <guid>https://velog.io/@sky-innerpeace/%ED%98%BC%EA%B3%B5%EB%8B%A8-7%EA%B8%B0-%ED%98%BC%EA%B3%B5JS-1%EC%A3%BC%EC%B0%A8</guid>
            <pubDate>Sat, 15 Jan 2022 16:36:43 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/sky-innerpeace/post/84e51f51-6ece-4dcf-bb3f-cd12d6f16432/%ED%94%BC%EA%B3%A44.png" alt="돌로...돌로레스"></p>
<p>공부하기 싫은 몸을 이끌고 일단 자리에 앉아 공부를 했다죠...
결심을 했죠... 다음 생에는 돌로 태어나기로 했다죠..</p>
<hr>

<h2 id="혼자-공부하는-자바스크립트-1주차">혼자 공부하는 자바스크립트 1주차</h2>
<h3 id="🎨-나만-보는-개념-정리">🎨 나만 보는 개념 정리</h3>
<p><del>모르거나 외우지 않고 이해하고 넘어가는 부분만 정리하기 때문에 개념 정리는 꽤나.. 부실합니다</del></p>
<blockquote>
<p><strong>ECMAScript</strong> : 1990년대 중반, 유럽컴퓨터제조협회(ECMA)에서 자바스크립트를 표준화함</p>
</blockquote>
<h4 id="1-무궁무진한-자바스크립트">1. 무궁무진한 자바스크립트</h4>
<ul>
<li>웹 클라이언트 애플리케이션</li>
<li>웹 서버 애플리케이션</li>
<li>모바일 애플리케이션</li>
<li>데스크톱 애플리케이션</li>
<li>데이터베이스 관리</li>
</ul>
<h4 id="2-웹-브라우저-점유율">2. 웹 브라우저 점유율</h4>
<p><a href="https://gs.statcounter.com/browser-version-market-share/desktop/south-korea"><img src="https://images.velog.io/images/sky-innerpeace/post/2ea89610-e73e-430a-b5b1-dbdecb32fd8e/image.png" alt=""></a></p>
<h4 id="3-개발환경-설정">3. 개발환경 설정</h4>
<p>텍스트 에디터 + 코드 실행기가 있으면 개발을 할 수 있다죠! 😆
텍스트 에디터로는 <strong>Visual Studil Code</strong>를, 코드 실행기는 <strong>Chrome</strong>을 사용해봅시다.</p>
<hr>

<h3 id="🎨-기본미션">🎨 기본미션</h3>
<h4 id="p54-파일-만들고-저장해-실행하기-에서-hello-world-출력하기">p.54 &lt;파일 만들고 저장해 실행하기&gt; 에서 &#39;Hello World&#39; 출력하기</h4>
<p><img src="https://images.velog.io/images/sky-innerpeace/post/46cbfdcd-94cb-4391-9eb3-c5164ecf9a77/image.png" alt=""></p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Document&lt;/title&gt;
    &lt;script&gt;
        alert(&#39;Hello World&#39;);
    &lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><hr>

<h3 id="🎨-선택미션">🎨 선택미션</h3>
<h4 id="chapter-0101-1-확인-문제-1번-상세하게-적고-인증샷">Chapter 01(01-1) 확인 문제 1번 상세하게 적고 인증샷</h4>
<blockquote>
<ol>
<li>인터넷을 돌아다니면서 보았던 쉽게 사용할 수 있고, 기능이 많다고 느꼈던 웹 사이트를 5개 정도 적어보세요.</li>
</ol>
<p>① 내가 사랑하는 <strong>YouTube</strong>
② 매일 기록하는 <strong>Notion</strong>
③ 밈 천국 <strong>Twitter</strong>
④ <strong>Discord</strong> 없인 못 살아
⑤ 검색은 해야지 <strong>Google</strong></p>
</blockquote>
<hr>

<p><img src="https://images.velog.io/images/sky-innerpeace/post/d308e5e3-921c-4100-aaf4-d8301f939741/%ED%94%BC%EA%B3%A45.png" alt="">
잔다고.. 잔다고....(뒤에서 갱얼쥐 우는 중)</p>
<p>안녕히계세요 여러분. 다음주 이시간에 뵐게요👏🏻</p>
]]></description>
        </item>
    </channel>
</rss>