<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>minkyeong-ko.log</title>
        <link>https://velog.io/</link>
        <description>기술로 감성을 자극하는 갬성개발자가 되고 싶어요</description>
        <lastBuildDate>Sun, 08 Aug 2021 18:20:17 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>minkyeong-ko.log</title>
            <url>https://images.velog.io/images/minkyeong-ko/profile/6a36561d-391f-4beb-9233-162fe0f07f32/rodin.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. minkyeong-ko.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/minkyeong-ko" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[포트폴리오] 미니 포트폴리오 웹사이트 제작기]]></title>
            <link>https://velog.io/@minkyeong-ko/%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4-%EB%AF%B8%EB%8B%88-%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4-%EC%9B%B9%EC%82%AC%EC%9D%B4%ED%8A%B8-%EC%A0%9C%EC%9E%91%EA%B8%B0</link>
            <guid>https://velog.io/@minkyeong-ko/%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4-%EB%AF%B8%EB%8B%88-%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4-%EC%9B%B9%EC%82%AC%EC%9D%B4%ED%8A%B8-%EC%A0%9C%EC%9E%91%EA%B8%B0</guid>
            <pubDate>Sun, 08 Aug 2021 18:20:17 GMT</pubDate>
            <description><![CDATA[<h2 id="제작-배경">제작 배경</h2>
<p>컴퓨터공학을 다중전공한 이후 여러 동아리와 팀 프로젝트,
개인 프로젝트들을 나름 열심히 해왔지만</p>
<p>뒤늦게 프론트엔드 개발자가 되겠다는 확실한 진로를 정하게 된 후에
해당 직무에 포트폴리오로 활용할 <strong>웹 개발 경험</strong>이 많지 않다는 것을 깨달았다.</p>
<p>지원서를 내기 위해서는 포트폴리오가 거의 필수이기 때문에,
어떻게든 제출을 하기 위해 노력해야 했다.</p>
<p>보통의 경우 진행한 프로젝트를 설명하는 형식이 대부분이지만 
경험이 다소 부족한 나이기에</p>
<p><strong>HTML/CSS/JS 활용능력</strong>과 <strong>웹 개발에 대한 열정</strong>을 
최대한 보여주는 것이 최선일 것이라 생각되어, 
조금은 빈약하더라도 나만의 웹사이트를 만들고자 마음먹게 되었다!</p>
<p>따라서 선택한 주제가 &#39;나&#39; 자신을 소개하는 웹사이트이다.</p>
<h2 id="결과물">결과물</h2>
<h4 id="전체-코드-보기👀">전체 코드 보기👀</h4>
<p><a href="https://github.com/Minkyeong-Ko/IntroWebsite">https://github.com/Minkyeong-Ko/IntroWebsite</a></p>
<h4 id="실제-웹사이트-보기👀">실제 웹사이트 보기👀</h4>
<p><a href="https://minkyeongko.netlify.app">https://minkyeongko.netlify.app</a></p>
<p>몇 주간 정말 열심히 만들어서, 배포까지 완료했다!
엉성한 점도 많고 아쉬운 점도 많지만,</p>
<p>기존에 계획했던 아래의 4가지 효과를 구현하는 데에 성공했다는 점은 매우 뿌듯하다.</p>
<h3 id="screen-shots">Screen Shots</h3>
<h4 id="1-텍스트-hover-효과">(1) 텍스트 hover 효과</h4>
<p><img src="https://images.velog.io/images/minkyeong-ko/post/b2b5db88-e9ec-4209-bd4d-b50cb9b74330/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-09%20%EC%98%A4%EC%A0%84%202.51.08.png" alt=""></p>
<h4 id="2-이미지-hover-효과">(2) 이미지 hover 효과</h4>
<p><img src="https://images.velog.io/images/minkyeong-ko/post/6391dbec-efca-49ad-bc09-9a743b82d877/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-09%20%EC%98%A4%EC%A0%84%202.30.29.png" alt=""></p>
<h4 id="3-스크롤에-따른-3d-cube의-회전">(3) 스크롤에 따른 3D Cube의 회전</h4>
<p><img src="https://images.velog.io/images/minkyeong-ko/post/010b84ea-fb1c-4f9b-92e2-22ce533be9cf/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-09%20%EC%98%A4%EC%A0%84%202.30.44.png" alt=""></p>
<h4 id="4-card-flip-애니메이션">(4) Card Flip 애니메이션</h4>
<p><img src="https://images.velog.io/images/minkyeong-ko/post/d3cd38e6-dc7e-43a2-8681-f7875be82a3b/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-09%20%EC%98%A4%EC%A0%84%202.32.44.png" alt=""></p>
<br>

<h2 id="반성과-계획의-시간">반성과 계획의 시간</h2>
<h4 id="반성">반성</h4>
<ul>
<li>전환 효과가 매끄럽지 않은 부분이 꽤 있다.</li>
<li>원하는 기능 구현에 급급해 User의 편의를 크게 고려하지 못한 것 같아서 아쉽다.</li>
<li>비슷한 이유로 내가 원하는 감성이나 디자인을 시도해 볼 여유를 갖지 못해서 매우 매우 아쉽다.</li>
<li>오직 chrome으로, 한 기기로만 개발한 아마추어적인 실수를 한 것 같다.</li>
<li>제작에 예상보다 시간이 많이 걸렸다.</li>
</ul>
<h4 id="앞으로의-계획">앞으로의 계획</h4>
<ul>
<li>반응형 웹사이트로 개편하기</li>
<li>Safari, IE 등 브라우저 호환성 높이기</li>
<li>깔끔한 코드 작성법 사용하기</li>
<li>이제 HTML, CSS, Javascript 고급 단계로 퀄리티 높이기</li>
</ul>
<br>

<blockquote>
<p>사실 프로젝트를 하다 보면 나의 부족한 점이 계속 더 보이는 것 같지만
그만큼 더 발전할 수 있는 기회가 주어지는 것이라 생각하고,
계속해서 열심히 달려 나가야겠다. 화이팅!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React Native] 문제 해결 - Scroll View 와 Flatlist 스크롤 버벅거리는 문제]]></title>
            <link>https://velog.io/@minkyeong-ko/React-Native-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0-Scroll-View-%EB%98%90%EB%8A%94-Flatlist-%EC%8A%A4%ED%81%AC%EB%A1%A4-%EB%B2%84%EB%B2%85%EA%B1%B0%EB%A6%BC</link>
            <guid>https://velog.io/@minkyeong-ko/React-Native-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0-Scroll-View-%EB%98%90%EB%8A%94-Flatlist-%EC%8A%A4%ED%81%AC%EB%A1%A4-%EB%B2%84%EB%B2%85%EA%B1%B0%EB%A6%BC</guid>
            <pubDate>Fri, 25 Jun 2021 07:50:12 GMT</pubDate>
            <description><![CDATA[<h2 id="스크롤이-되지-않는-scrollview">스크롤이 되지 않는 ScrollView</h2>
<p>프로젝트 진행 중에 <code>ScrollView</code> 를 사용했는데 스크롤이 되지 않거나 굉장히 버벅되는 현상이 발생했다.</p>
<p>검색을 해 보니 대부분 <code>flex</code> 또는 <code>flexwrap</code> 설정을 바꿔주면 해결된다고 나와 있었지만 나에게는 전혀 통하지 않았다.</p>
<p>그러던 중 StackOverflow에서 <code>TouchableWithoutFeedback</code> 으로 ScrollView를 감싸 보라는 댓을 발견, 적용했더니 바로 해결되어 버렸다!</p>
<h2 id="이유는">이유는?</h2>
<p>아마도 현재 프로젝트에서 화면 전체를 이미 <code>TouchableWithoutFeedback</code> 으로 감싼 상태였던 것이 문제가 된 것 같다.</p>
<p><code>ScrollView</code> 를 스크롤을 할 때의 터치 이벤트가 부모 touchable 요소인 <code>TouchableWithoutFeedback</code> 으로 가버린 것으로 추측된다.</p>
<p>이 때 <code>onStartShouldSetResponder</code> 를 사용해서 propagation을 막는 방법도 있다고 하는데 나는 그냥 Touchable로 감쌌을 때 바로 해결이 됐다!</p>
<p>아무튼 <code>ScrollView</code> 내에서, 그리고 <code>Flatlist</code> 의 경우 <code>renderItem</code> 에서 요소를 TouchableWithoutFeedback으로 감싸주면 아주 잘 동작한다ㅎㅎ</p>
<pre><code class="language-js">&lt;ScrollView&gt;
    &lt;TouchableWithoutFeedback &gt;
        ...
    &lt;TouchableWithoutFeedback/&gt;
&lt;ScrollView/&gt;</code></pre>
<p>이런 식으로..!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React Native] 인스타그램 같은 좋아요 효과 구현하기 (Double tap, Animated)]]></title>
            <link>https://velog.io/@minkyeong-ko/React-Native-%EC%9D%B8%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%EA%B0%99%EC%9D%80-Like-%ED%9A%A8%EA%B3%BC-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-Double-tap-Animated</link>
            <guid>https://velog.io/@minkyeong-ko/React-Native-%EC%9D%B8%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%EA%B0%99%EC%9D%80-Like-%ED%9A%A8%EA%B3%BC-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-Double-tap-Animated</guid>
            <pubDate>Thu, 17 Jun 2021 13:48:23 GMT</pubDate>
            <description><![CDATA[<h2 id="구현-과정">구현 과정</h2>
<p>최근 개인 프로젝트에서 좋아요 기능이 필요했다.
그래서 인스타그램처럼 사진을 두번 클릭하거나 하트 아이콘을 누르면 하트가 떴다가 사라지는 효과를 구현했고, 이를 정리해보려 한다.</p>
<p>간단한 기능 구현이지만 만들면서 만약 내가 SNS를 만든다면..? 하는 질문을 해보게 되었고 만약 만든다면 &#39;필름 사진을 공유 플랫폼&#39; 을 만들지 않을까 하는 생각으로.. 엉성하지만 재밌게 개발했다ㅎㅎ</p>
<p>+오늘도 사진은 저작권 필요없는 photo by 나!</p>
<br>

<h2 id="화면-구성하기">화면 구성하기</h2>
<pre><code class="language-js">//App.js
import React from &quot;react&quot;;
import { StyleSheet, View, Text, SafeAreaView, ImageBackground } from &#39;react-native&#39;;
import Icon from &#39;react-native-vector-icons/AntDesign&#39;;

function App() {
  return (
    &lt;SafeAreaView style={styles.container}&gt;

      {/* 제목과 좋아요 버튼 */}
      &lt;View style={[styles.section, {flex: 1}]}&gt;
        &lt;View style={styles.title}&gt;
          &lt;Text style={styles.titleText}&gt;The Garden of The Royal Library&lt;/Text&gt;
          &lt;Icon name=&quot;hearto&quot; size={23} color={&#39;#3D3D3D&#39;}&gt;&lt;/Icon&gt;
        &lt;/View&gt;
      &lt;/View&gt;

      {/* 사진 */}
      &lt;View style={[styles.section, {flex: 6, padding: 15}]}&gt;
          &lt;ImageBackground 
            resizeMode=&#39;contain&#39;
            source={require(&#39;./denmark.jpg&#39;)}
            style={{
              width: &#39;100%&#39;,
              height: &#39;100%&#39;,
            }}
          /&gt;
        &lt;View style={{position: &#39;absolute&#39;}}&gt;
          &lt;Icon name=&quot;heart&quot; size={50} color={&#39;white&#39;}&gt;&lt;/Icon&gt;
        &lt;/View&gt;
      &lt;/View&gt;

      {/* 설명과 해시태그 */}
      &lt;View style={[styles.section, {flex: 2, borderBottomWidth: 0.8}]}&gt;
        &lt;View style={{width: &#39;90%&#39;, }}&gt;
          &lt;View style={styles.user}&gt;
            &lt;View style={styles.userimage} /&gt;
            &lt;Text style={styles.username}&gt; Username &lt;/Text&gt;
          &lt;/View&gt;
          &lt;Text style={styles.text}&gt;똑딱이 필름카메라와 함께&lt;/Text&gt;
          &lt;Text style={styles.text}&gt;덴마크 여행중&lt;/Text&gt;
          &lt;Text style={styles.tags}&gt;#fuji200  #copenhagen&lt;/Text&gt;
        &lt;/View&gt;
      &lt;/View&gt;
    &lt;/SafeAreaView&gt;
  );

}

const styles = StyleSheet.create({
  container: {
    flex: 1, 
    marginHorizontal: 17,
  },
  section: {
    borderTopWidth: 0.8, 
    borderColor: &#39;gray&#39;, 
    justifyContent: &#39;center&#39;, 
    alignItems: &#39;center&#39;,
  },
  title: {
    width: &#39;90%&#39;, 
    flexDirection: &#39;row&#39;, 
    justifyContent: &#39;space-between&#39;, 
    alignItems: &#39;center&#39;,
  },
  titleText: {
    fontSize: 17, 
    fontFamily: &#39;SpoqaHanSansNeo-Regular&#39;,
  },

  user: {
    flexDirection: &#39;row&#39;, 
    alignItems: &#39;center&#39;, 
    marginVertical: 5, 
  },
  userimage: {
    width: 32, 
    height: 32, 
    borderRadius: 32/2, 
    backgroundColor: &#39;black&#39;, 
    marginHorizontal: 5,
  },  
  username: {
    fontSize: 15, 
    fontWeight: &#39;500&#39;, 
    fontFamily: &#39;SpoqaHanSansNeo-Regular&#39;,
  },
  text: {
      fontSize: 15, 
      marginVertical: 5, 
      fontFamily: &#39;SpoqaHanSansNeo-Light&#39;,
  },
  tags: {
    fontSize: 15, 
    color: &#39;green&#39;, 
    marginVertical: 5,
  },
}
);

export default App;</code></pre>
<p>간단하게 제목, 사진, 설명으로 나눠진 임시 레이아웃을 구성했다.</p>
<p><img src="https://images.velog.io/images/minkyeong-ko/post/a5c28e10-b786-43eb-aa84-f57639a75adf/Simulator%20Screen%20Shot%20-%20iPhone%2012%20Pro%20-%202021-06-16%20at%2018.13.56.png" alt="">
제목 옆의 하트 아이콘을 누르거나 사진을 더블 탭 하면 사진 중간의 하트 아이콘이 서서히 떴다가 사라지는 효과를 구현할 것이다.</p>
<br>

<h2 id="double-tap-구현">Double Tap 구현</h2>
<p>우선 사진에다가 더블 탭을 해야 하니,
이미지를 TouchableWithoutFeedback으로 감싸서 터치가 가능하게 만든다.</p>
<p>TouchableWithoutFeedback은 말 그대로 터치를 했을 때 opacity가 바뀌거나 하는 효과를 보여주지 않는 것이다.</p>
<pre><code class="language-js">...
&lt;TouchableWithoutFeedback
  onPress={handleDoubleTap}
&gt;
  &lt;ImageBackground 
    resizeMode=&#39;contain&#39;
    source={require(&#39;./denmark.jpg&#39;)}
    style={{
      width: &#39;100%&#39;,
      height: &#39;100%&#39;,
    }}
  /&gt;
&lt;/TouchableWithoutFeedback&gt;
...</code></pre>
<p>이제 사진을 터치하면 handleDoubleTap 이라는 함수를 실행하게 되고 그 안에서 터치가 그냥 터치인지, 더블 탭인지 구별해야 한다.</p>
<pre><code class="language-js">//App.js
...
  var lastTap = null;  

  const handleDoubleTap = () =&gt; {
    const now = Date.now();
    const DOUBLE_PRESS_DELAY = 300;
    //두번째 tap이 지난 tap을 한지 0.03초 이내에 이뤄졌을 때 -&gt; Double tap
    if (lastTap &amp;&amp; (now - lastTap) &lt; DOUBLE_PRESS_DELAY) {
      toggleHeart();
    }
    else {
      lastTap = now;
    }
  }
  ...</code></pre>
<p>빠르게 두번의 터치가 들어오면(여기선 0.03초 이내) 이를 더블탭으로 인식하게 되고,
하트 Animation을 보여줄 toggleHeart 함수를 실행한다. </p>
<pre><code class="language-js">//App.js
...
const [heart, setHeart] = useState(false);

const toggleHeart = () =&gt; {
    setHeart(previousState =&gt; !previousState);
    fillHeart();
  }
...</code></pre>
<br>

<h3 id="fillheart-애니메이션">fillHeart 애니메이션</h3>
<h4 id="animation을-위한-코드-수정">Animation을 위한 코드 수정</h4>
<p>우선 애니메이션 코드를 작성하기 전에 heart의 값이 true이면 채워진 하트를, false면 테두리만 있는 하트 아이콘을 보여주도록 코드를 수정한다.</p>
<p>제목 옆의 하트 아이콘은 클릭 했을 때 Animation을 보여줘야 하니 TouchableOpacity로 감싸서 터치 시 toggleHeart 함수가 실행되게 한다.</p>
<pre><code class="language-js">//App.js
...
&lt;View style={styles.title}&gt;
  &lt;Text style={styles.titleText}&gt;The Garden of The Royal Library&lt;/Text&gt;

  {/* Touchable로 감싸기 */}
  &lt;TouchableOpacity
    onPress={toggleHeart}
    style={{
      width: 25,
      height: 25,
    }}
  &gt;
    {/* heart값에 따른 아이콘 변경 */}
    {heart ? &lt;Icon name=&quot;heart&quot; size={23} color={&#39;#3D3D3D&#39;}&gt;&lt;/Icon&gt; : &lt;Icon name=&quot;hearto&quot; size={23} color={&#39;#595959&#39;}&gt;&lt;/Icon&gt;}
  &lt;/TouchableOpacity&gt;
&lt;/View&gt;
...</code></pre>
<p>아래의 아이콘은 하트가 보여졌다가 사라지는 효과를 만들고 보여주기 위해,
View를 Animated.View로 변경한 뒤 style에 opacity 값을 추가한다.</p>
<pre><code class="language-js">//App.js
...
{/* Animated View로 변경, opacity 추가 */}
&lt;Animated.View style={{position: &#39;absolute&#39;, opacity: opacity}}&gt;
  {/* heart값에 따른 아이콘 변경 */}
  {heart ? &lt;Icon name=&quot;heart&quot; size={50} color={&#39;white&#39;}&gt;&lt;/Icon&gt; : &lt;Icon name=&quot;hearto&quot; size={50} color={&#39;white&#39;}&gt;&lt;/Icon&gt;}
&lt;/Animated.View&gt;
&lt;/View&gt;
...</code></pre>
<p>이제 Animated 함수를 활용해서 하트가 떴다가 사라지는 효과를 구현할 수 있다. </p>
<h4 id="fillheart-animation-구현하기">fillHeart Animation 구현하기</h4>
<pre><code class="language-js">//App.js
...
const opacity = useRef(new Animated.Value(0)).current;

const fillHeart = () =&gt; {
    Animated.sequence([
      Animated.timing(opacity, {
        toValue: 1,
        duration: 400,
        easing: Easing.quad,
        useNativeDriver: true,
      }),
      Animated.delay(600),
      Animated.timing(opacity, {
        toValue: 0,
        duration: 500,
        useNativeDriver: true,
      }),
    ]).start();
  }
...</code></pre>
<p>이렇게 구현한 하트 애니메이션을 크게 세 가지로 생각할 수 있다.</p>
<p>하트 아이콘이 부드럽게 떠오르고,
잠깐 동안 떠 있다가,
다시 부드럽게 사라진다.</p>
<p><strong>Animated.timing</strong>을 사용해 400ms 동안 0이었던 opacity를 1(toValue)로 만들고,
<strong>Animated.delay</strong>를 사용해 600ms 동안 하트 아이콘을 보여주고,
다시 <strong>Animated.timing</strong>을 사용해 1이 된 opacity를 다시 0(toValue)로 만들어 하트 이모티콘을 사라지게 만든다.</p>
<p>그리고 마지막으로 <strong>Animated.sequence</strong>를 사용해 위의 세 애니메이션을 연속적으로 실행한다.</p>
<p>여기서 바뀌는 opacity 값은 아까 추가한 Animated.View의 opacity 값에 바로 반영되게 된다.</p>
<p>끝!</p>
<br>

<h2 id="전체-코드-보기👀">전체 코드 보기👀</h2>
<pre><code class="language-js">//App.js
import React, 
{ useRef, useState } 
from &quot;react&quot;;
import { StyleSheet, View, Text, SafeAreaView, ImageBackground,
        Animated, TouchableWithoutFeedback, TouchableOpacity, Easing,
} from &#39;react-native&#39;;

import Icon from &#39;react-native-vector-icons/AntDesign&#39;;

function App() {

  const [heart, setHeart] = useState(false);
  const opacity = useRef(new Animated.Value(0)).current;

  var lastTap = null;  

  const handleDoubleTap = () =&gt; {
    const now = Date.now();
    const DOUBLE_PRESS_DELAY = 300;
    //두번째 tap이 지난 tap을 한지 0.03초 이내에 이뤄졌을 때 -&gt; Double tap
    if (lastTap &amp;&amp; (now - lastTap) &lt; DOUBLE_PRESS_DELAY) {
      toggleHeart();
    }
    else {
      lastTap = now;
    }
  }

  const toggleHeart = () =&gt; {
    setHeart(previousState =&gt; !previousState);
    fillHeart();
  }

  const fillHeart = () =&gt; {
    Animated.sequence([
      Animated.timing(opacity, {
        toValue: 1,
        duration: 400,
        easing: Easing.quad,
        useNativeDriver: true,
      }),
      Animated.delay(600),
      Animated.timing(opacity, {
        toValue: 0,
        duration: 500,
        useNativeDriver: true,
      }),
    ]).start();
  }

  return (
    &lt;SafeAreaView style={styles.container}&gt;
      &lt;View style={[styles.section, {flex: 1}]}&gt;
        &lt;View style={styles.title}&gt;
          &lt;Text style={styles.titleText}&gt;The Garden of The Royal Library&lt;/Text&gt;
          &lt;TouchableOpacity
            onPress={toggleHeart}
            style={{
              width: 25,
              height: 25,
            }}
          &gt;
            {heart ? &lt;Icon name=&quot;heart&quot; size={23} color={&#39;#3D3D3D&#39;}&gt;&lt;/Icon&gt; : &lt;Icon name=&quot;hearto&quot; size={23} color={&#39;#595959&#39;}&gt;&lt;/Icon&gt;}
          &lt;/TouchableOpacity&gt;
        &lt;/View&gt;
      &lt;/View&gt;
      &lt;View style={[styles.section, {flex: 6, padding: 15}]}&gt;
        &lt;TouchableWithoutFeedback
          onPress={handleDoubleTap}
        &gt;
          &lt;ImageBackground 
            resizeMode=&#39;contain&#39;
            source={require(&#39;./denmark.jpg&#39;)}
            style={{
              width: &#39;100%&#39;,
              height: &#39;100%&#39;,
            }}
          /&gt;
        &lt;/TouchableWithoutFeedback&gt;
        {/* Animated로 변경, opacity 값 */}
        &lt;Animated.View style={{position: &#39;absolute&#39;, opacity: opacity}}&gt;
          {heart ? &lt;Icon name=&quot;heart&quot; size={50} color={&#39;white&#39;}&gt;&lt;/Icon&gt; : &lt;Icon name=&quot;hearto&quot; size={50} color={&#39;white&#39;}&gt;&lt;/Icon&gt;}
        &lt;/Animated.View&gt;
      &lt;/View&gt;
      &lt;View style={[styles.section, {flex: 2, borderBottomWidth: 0.8}]}&gt;
        &lt;View style={{width: &#39;90%&#39;, }}&gt;
          &lt;View style={styles.user}&gt;
            &lt;View style={styles.userimage} /&gt;
            &lt;Text style={styles.username}&gt; Username &lt;/Text&gt;
          &lt;/View&gt;
          &lt;Text style={styles.text}&gt;똑딱이 필름카메라와 함께&lt;/Text&gt;
          &lt;Text style={styles.text}&gt;덴마크 여행중&lt;/Text&gt;
          &lt;Text style={styles.tags}&gt;#fuji200  #copenhagen&lt;/Text&gt;
        &lt;/View&gt;
      &lt;/View&gt;
    &lt;/SafeAreaView&gt;
  );

}

const styles = StyleSheet.create({
  container: {
    flex: 1, 
    marginHorizontal: 17,
  },
  section: {
    borderTopWidth: 0.8, 
    borderColor: &#39;gray&#39;, 
    justifyContent: &#39;center&#39;, 
    alignItems: &#39;center&#39;,
  },
  title: {
    width: &#39;90%&#39;, 
    flexDirection: &#39;row&#39;, 
    justifyContent: &#39;space-between&#39;, 
    alignItems: &#39;center&#39;,
  },
  titleText: {
    fontSize: 17, 
    fontFamily: &#39;SpoqaHanSansNeo-Regular&#39;,
  },

  user: {
    flexDirection: &#39;row&#39;, 
    alignItems: &#39;center&#39;, 
    marginVertical: 5, 
  },
  userimage: {
    width: 32, 
    height: 32, 
    borderRadius: 32/2, 
    backgroundColor: &#39;black&#39;, 
    marginHorizontal: 5,
  },  
  username: {
    fontSize: 15, 
    fontWeight: &#39;500&#39;, 
    fontFamily: &#39;SpoqaHanSansNeo-Regular&#39;,
  },
  text: {
      fontSize: 15, 
      marginVertical: 5, 
      fontFamily: &#39;SpoqaHanSansNeo-Light&#39;,
  },
  tags: {
    fontSize: 15, 
    color: &#39;green&#39;, 
    marginVertical: 5,
  },
}
);

export default App;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Javascript] 스크롤에 따른 배경색 변경]]></title>
            <link>https://velog.io/@minkyeong-ko/Javascript-%EC%8A%A4%ED%81%AC%EB%A1%A4%EC%97%90-%EB%94%B0%EB%A5%B8-%EB%B0%B0%EA%B2%BD%EC%83%89-%EB%B3%80%EA%B2%BD</link>
            <guid>https://velog.io/@minkyeong-ko/Javascript-%EC%8A%A4%ED%81%AC%EB%A1%A4%EC%97%90-%EB%94%B0%EB%A5%B8-%EB%B0%B0%EA%B2%BD%EC%83%89-%EB%B3%80%EA%B2%BD</guid>
            <pubDate>Fri, 21 May 2021 12:48:58 GMT</pubDate>
            <description><![CDATA[<h2 id="indexjs">index.js</h2>
<pre><code class="language-javascript">//index.js
var colors = [
    &#39;#F2B705&#39;,
    &#39;#F25C05&#39;,
    &#39;#0388A6&#39;,
    &#39;#0E5929&#39;,
    &#39;#F272A1&#39;,
]

var container = document.getElementById(&#39;container&#39;);

var text = document.getElementById(&#39;value&#39;);    //Scroll Value
var color = document.getElementById(&#39;color&#39;);    //Color Code

//Wheel Event
container.onwheel = changeBgColor;

var colorIndex = 0;
var scrollValue = 0;    //get mouse wheel value

function changeBgColor(e) {
    scrollValue += e.deltaY * 0.01;
    text.textContent = Math.floor(scrollValue);

    console.log(scrollValue);

    //위로 Scroll
    if (scrollValue &gt; 10) {
        colorIndex += 1;
        if (colorIndex &gt; colors.length-1) colorIndex = 0;
        color.textContent = colors[colorIndex];
        container.style.backgroundColor = colors[colorIndex];    //배경색 변경
        scrollValue = 0;    //스크롤 값 초기화
    }

    //아래로 Scroll
    if (scrollValue &lt; -10) {
        colorIndex -= 1;
        if (colorIndex &lt; 0) colorIndex = colors.length-1;
        color.textContent = colors[colorIndex];
        container.style.backgroundColor = colors[colorIndex];    //배경색 변경
        scrollValue = 0;    //스크롤 값 초기화
    }
       e.preventDefault(); // disable the actual scrolling
}</code></pre>
<p>스크롤 동작이 이뤄질 때마다 e.deltaY 값을 누적시킵니다.
위로 스크롤 시 양수 값을, 아래로 스크롤 시 음수 값을 가집니다.</p>
<p>누적 값이 일정 수준이 넘어가면 colorIndex 값을 증가시켜 colors 배열의 다음 색으로 배경색을 변경합니다.</p>
<p>그리고 부드러운 트랜지션을 위해 css 코드를 추가합니다.</p>
<pre><code class="language-css">transition: background-color 0.8s;</code></pre>
<br>

<h2 id="datenow">Date.now()</h2>
<p>만약 스크롤을 빠르게 넘긴다면, 한번에 배경 색이 여러번 바뀌게 됩니다.
괜찮을 수도 있지만 저는 정신이 없기도 하고 좀 더 안정적으로 동작하게 하고 싶어서 추가로 Date()를 사용했습니다.</p>
<p>현재 시간을 나타내는 변수 dateNow를 정의하고</p>
<pre><code class="language-js">var dateNow = Date.now();</code></pre>
<p>스크롤 값이 들어오면 흐른 시간을 비교하고, 일정 시간이 넘어야만 새롭게 배경 색깔을 변경합니다.</p>
<pre><code class="language-js">timePassed = Date.now() - dateNow;
if (timePassed &gt; 500) {
    ...
}</code></pre>
<p>배경 색이 바뀌는 순간에 다시 현재 시간을 저장합니다.</p>
<pre><code class="language-js">dateNow = Date.now();</code></pre>
<br>

<h2 id="전체-코드-보기👀">전체 코드 보기👀</h2>
<p>!codepen[Mila00a/embed/bGqgJZO?height=517&amp;theme-id=light&amp;default-tab=js,result]</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React Native] 오른쪽 스와이프 시 텍스트 보여주기(Swipeable, Interpolation)]]></title>
            <link>https://velog.io/@minkyeong-ko/React-Native-%EC%98%A4%EB%A5%B8%EC%AA%BD-%EC%8A%A4%EC%99%80%EC%9D%B4%ED%94%84-%EC%8B%9C-%ED%85%8D%EC%8A%A4%ED%8A%B8-%EB%B3%B4%EC%97%AC%EC%A3%BC%EA%B8%B0Swipeable</link>
            <guid>https://velog.io/@minkyeong-ko/React-Native-%EC%98%A4%EB%A5%B8%EC%AA%BD-%EC%8A%A4%EC%99%80%EC%9D%B4%ED%94%84-%EC%8B%9C-%ED%85%8D%EC%8A%A4%ED%8A%B8-%EB%B3%B4%EC%97%AC%EC%A3%BC%EA%B8%B0Swipeable</guid>
            <pubDate>Fri, 21 May 2021 09:08:18 GMT</pubDate>
            <description><![CDATA[<h2 id="과정-설명">과정 설명</h2>
<p>위의 예시는 제가 직접 찍은 사진을 가지고,
오른쪽에서 왼쪽으로 화면을 Swipe 했을 때 작품 이름이랑 작가 정보가 나타나도록 만든 것입니다.</p>
<p><strong>오늘의 구현 순서</strong></p>
<blockquote>
<ol>
<li>스와이프 할 화면 만들기(Swipeable, ImageBackground)</li>
<li>rightAction 스와이프 동작 정의하기(Interpolation)</li>
</ol>
</blockquote>
<p><strong>이 글을 쓰는 이유?</strong></p>
<blockquote>
<p>저처럼 Interpolation이 많이 헷갈리시는 분들, 예시 코드가 많은 왼쪽이 아닌 오른쪽 스와이프를 구현하고 싶으신 분들에게 작은 도움이 되길 바라며..!</p>
</blockquote>
<br>

<h2 id="스와이프-할-화면-만들기">스와이프 할 화면 만들기</h2>
<pre><code class="language-bash">import Swipeable from &#39;react-native-gesture-handler/Swipeable&#39;;</code></pre>
<p>Swipeable을 사용하기 위해 별도로 import를 해줍니다.(react-native-gesture-handler 필요)</p>
<h3 id="imagebackground">ImageBackground</h3>
<p>우선 제일 먼저 화면을 꽉 채우는 이미지를 만들고, 그 이미지를 Swipeable하게 만들어야 합니다.
여러 가지 방법이 있겠지만 저는 ImageBackground 안에 Swipeable을 넣고 width, height을 100%로 만들었습니다.</p>
<pre><code class="language-javascrtipt">&lt;View style={{width: &#39;100%&#39;, height: &#39;100%&#39;,}}&gt;
      &lt;ImageBackground 
          source={url} 
          style={{
            resizeMode: &#39;contain&#39;,
            position: &#39;absolute&#39;,
            top: 0,
            left: 0,
            right: 0,
            bottom: 0,
          }}
      &gt;
          &lt;Swipeable
            containerStyle={{
              width: &#39;100%&#39;,
              height: &#39;100%&#39;,
            }}
            renderRightActions={rightAction}
          &gt;&lt;/Swipeable&gt;
      &lt;/ImageBackground&gt;
&lt;/View&gt;</code></pre>
<p>그리고 오른쪽에서 Swipe가 감지되면 rightAction 함수를 호출하도록 합니다.</p>
<br>

<h2 id="rightaction-함수-정의하기">rightAction 함수 정의하기</h2>
<pre><code class="language-javascript">const rightAction = (progress, dragX) =&gt; {
      const trans = dragX.interpolate({
          inputRange: [-window.width, 0],   //화면의 왼쪽 끝, 화면의 맨 오른쪽
        outputRange: [0, window.width],   //transitin 0, transition 100% 
      });

      return (
        &lt;View style={{flex: 1, justifyContent: &#39;center&#39;, alignItems: &#39;center&#39;, zIndex: 10,}}&gt;
          //작품 정보
          &lt;Animated.Text 
            style={{
              fontSize: 60, 
              color: &#39;white&#39;,  
              fontWeight: &#39;500&#39;,
              fontFamily: &#39;LibreBaskerville-Bold&#39;,
              transform: [{ translateX: trans }], 
              }}
            &gt;
              The Thinker
              Bronze 1900-01 (1880)
          &lt;/Animated.Text&gt;
          //작가 정보
          &lt;Animated.Text 
            style={{
              fontSize: 22, 
              color: &#39;white&#39;, 
              fontWeight: &#39;200&#39;,
              transform: [{ translateX: trans }], 
            }}
          &gt;
            Auguste Rodin (French, 1840-1917)
          &lt;/Animated.Text&gt;
        &lt;/View&gt;
      );
}</code></pre>
<p>App.js에 정의한 rightAction 코드입니다.</p>
<h3 id="interpolation">Interpolation</h3>
<p>interpolate는 간단히 말하면 inputRange를 outputRange로 매핑시키는 함수입니다.</p>
<pre><code class="language-javascript">const trans = dragX.interpolate({
          inputRange: [-window.width, 0],   //화면의 왼쪽 끝, 화면의 맨 오른쪽
        outputRange: [0, window.width],   //transition 0, transition 100% 
      });</code></pre>
<p>따라서 해당 코드는 input인 dragX에 따라, output인 trans의 값이 매핑됩니다.</p>
<h4 id="dragx">dragX</h4>
<p>interpolate()의 input이자 오른쪽 &lt;-&gt; 왼쪽 스와이프 정도를 나타내는 값입니다.
dragX의 값을 콘솔로 확인해 보면 오른쪽 -&gt; 왼쪽으로 스와이프 시 값이 0에서 마이너스로 변하는 것을 확인할 수 있습니다.</p>
<p>따라서 </p>
<pre><code class="language-javascript">inputRange: [-window.width, 0]</code></pre>
<p>의 뜻은 화면의 왼쪽 끝과 오른쪽 끝이 Swipe의 범위가 되도록 했습니다.</p>
<h4 id="trans">trans</h4>
<p>interpolate()의 output이자 Swipe를 했을 때 화면에 보일 텍스트의 translateX 값입니다.</p>
<pre><code class="language-javascript">outputRange: [0, window.width],</code></pre>
<p>translateX가 0 이라면 텍스트를 원래자리 그대로 보여주고, translateX가 window 크기라면 텍스트가 화면의 오른쪽으로 완전히 숨겨지는 상태입니다.</p>
<p>따라서 input과 output이 매핑된다는 것은 
스와이프를 화면의 왼쪽 끝(dragX : -window.width)까지 했을 때 텍스트를 보여주고(translateX : 0), 
오른쪽 끝(dragX : 0)까지 했을 때는 텍스트를 오른쪽으로 완전히 숨기는(translateX : window.width) 것입니다.</p>
<br>

<p>사실 저도 interpolation은 처음이라,
interpolation에 대한 설명이라기 보다는 제가 작성한 코드를 설명한 것에 불과한 것 같기도 합니다..
range 구간이 3개 이상인 interpolation도 사용해 보면서 더 공부해야 할 것 같습니다.</p>
<br>

<h2 id="전체-코드-보기👀">전체 코드 보기👀</h2>
<pre><code class="language-javascript">import React from &#39;react&#39;;
import { View, Animated, ImageBackground, } from &#39;react-native&#39;;

import Swipeable from &#39;react-native-gesture-handler/Swipeable&#39;;
import { Dimensions } from &#39;react-native&#39;;

const window = Dimensions.get(&quot;window&quot;);
const url = require(&#39;./rodin.jpeg&#39;)


function App() {
    const rightAction = (progress, dragX) =&gt; {
      console.log(dragX);
      const trans = dragX.interpolate({
        inputRange: [-window.width, 0],   //화면의 왼쪽 끝, 화면의 맨 오른쪽
        outputRange: [0, window.width],   //transitin 0, transition 100% 
      });

      return (
        &lt;View style={{flex: 1, justifyContent: &#39;center&#39;, alignItems: &#39;center&#39;, zIndex: 10,}}&gt;
          &lt;Animated.Text 
            style={{
              fontSize: 60, 
              color: &#39;white&#39;,  
              fontWeight: &#39;500&#39;,
              fontFamily: &#39;LibreBaskerville-Bold&#39;,
              transform: [{ translateX: trans }], 
              }}
            &gt;
              The Thinker
              Bronze 1900-01 (1880)
            &lt;/Animated.Text&gt;
          &lt;Animated.Text 
            style={{
              fontSize: 22, 
              color: &#39;white&#39;, 
              fontWeight: &#39;200&#39;,
              transform: [{ translateX: trans }], 
            }
          }&gt;
            Auguste Rodin (French, 1840-1917)
          &lt;/Animated.Text&gt;
        &lt;/View&gt;
      );
    }

    return (
      &lt;View style={{width: &#39;100%&#39;, height: &#39;100%&#39;,}}&gt;
        &lt;ImageBackground 
          source={url} 
          style={{
            resizeMode: &#39;contain&#39;,
            position: &#39;absolute&#39;,
            top: 0,
            left: 0,
            right: 0,
            bottom: 0,
          }}
        &gt;
          &lt;Swipeable
            containerStyle={{
              width: &#39;100%&#39;,
              height: &#39;100%&#39;,
            }}
            renderRightActions={rightAction}
          &gt;
          &lt;/Swipeable&gt;
        &lt;/ImageBackground&gt;
      &lt;/View&gt;
    );
}

export default App;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React Native] Drawer와 Stack Navigatior 함께 사용하기]]></title>
            <link>https://velog.io/@minkyeong-ko/React-Native-Drawer%EC%99%80-Stack-%ED%95%A8%EA%BB%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@minkyeong-ko/React-Native-Drawer%EC%99%80-Stack-%ED%95%A8%EA%BB%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 18 May 2021 10:55:20 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/minkyeong-ko/post/01361004-b33c-49ee-a8fc-56aa66a75477/main.gif" alt=""></p>
<br>

<h2 id="react-navigation">React Navigation</h2>
<p>React Navigation은 앱 안에서의 화면 전환을 지원하는 라이브러리이다.
공식 문서(<a href="https://reactnavigation.org/docs/getting-started">https://reactnavigation.org/docs/getting-started</a>)를 따라 하면 쉽게 설치할 수 있다.</p>
<h3 id="drawer-navigation">Drawer Navigation</h3>
<pre><code class="language-bash">npm install @react-navigation/drawer</code></pre>
<p>Drawer Navigation은 화면 왼쪽 또는 오른쪽의 화면 리스트를 통해서 화면을 이동할 수 있는 기능.
<img src="https://images.velog.io/images/minkyeong-ko/post/f6187755-6d84-4442-9da8-4fc258a02858/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-05-18%20%EC%98%A4%ED%9B%84%207.02.42.png" alt="">
쉽게 말해 화면을 선택하는 메뉴 같은 느낌이다.</p>
<h3 id="stack-navigator">Stack Navigator</h3>
<pre><code class="language-bash">npm install @react-navigation/stack</code></pre>
<p>Stack Navigator를 사용하면 새로운 화면으로 이동할 때마다 Stack의 맨 위에 쌓여서, 순차적인 앞뒤 화면 이동이 가능하다.
<img src="https://images.velog.io/images/minkyeong-ko/post/dc754f64-4600-41c3-9349-2d451b0ba657/stack.gif" alt=""></p>
<br>

<h2 id="구현-과정과-개념-설명">구현 과정과 개념 설명</h2>
<p>사실 Drawer와 Stack을 각각 구현하는 건 어렵지 않을 텐데 프로젝트에 Drawer랑 Stack을 같이 사용했을 때 좀 헷갈렸던 기억이 있어서 제대로 정리하고자 글을 쓰게 됐다.</p>
<p>예시로 만든 앱에는, 간단하게 HomeScreen, DetailScreen, AboutScreen 세 가지 화면이 있다.</p>
<p>Drawer 메뉴에서는 Home, About 화면에만 접근이 가능하고
Details 화면은 Home을 통해서만 접근이 가능하게 만들고자 한다.</p>
<h4 id="따라서-필요한-과정은">따라서 필요한 과정은,</h4>
<blockquote>
<ol>
<li>StackNavigator 정의(Home, Details)</li>
<li>DrawerNavigator 정의(Home, About)</li>
<li>App.js에서 DrawerNavigator 호출</li>
</ol>
</blockquote>
<br>

<h3 id="stacknavigator-home-details">StackNavigator (Home, Details)</h3>
<pre><code class="language-javascript">/* StackNavigator.js */
import React from &quot;react&quot;;
import { createStackNavigator } from &quot;@react-navigation/stack&quot;;

import Home from &#39;./HomeScreen&#39;;
import Details from &#39;./DetailScreen&#39;;

const Stack = createStackNavigator();

const StackNavigator = () =&gt; {
  return (
    &lt;Stack.Navigator&gt;
      &lt;Stack.Screen name=&quot;Home&quot; component={Home} /&gt;
      &lt;Stack.Screen name=&quot;Details&quot; component={Details} /&gt;
    &lt;/Stack.Navigator&gt;
  );
}

export { StackNavigator };</code></pre>
<p>StackNavigator.js에 Stack 형식으로 동작할 화면들인 Home과 Details를 정의했다.</p>
<br>


<pre><code class="language-javascript">import React from &#39;react&#39;;
import { Text, View, StyleSheet, Button } from &#39;react-native&#39;;

function HomeScreen({ navigation }) {
  return (
    &lt;View style={styles.container}&gt;
      &lt;Text&gt;HomeScreen&lt;/Text&gt;
      &lt;Button title=&quot;Go to Details&quot; onPress={() =&gt; navigation.navigate(&#39;Details&#39;)} /&gt;
    &lt;/View&gt;
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1, 
    justifyContent: &#39;center&#39;, 
    alignItems: &#39;center&#39;,
  },
});

export default HomeScreen;</code></pre>
<p>그리고 Details 화면으로 이동하기 위한 버튼을 HomeScreen에 생성.
Stack에 화면이 쌓여있기 때문에 Details 화면에서 뒤로 이동하면 HomeScreen이 된다.</p>
<br>

<h3 id="drawernavigator-정의home-about">DrawerNavigator 정의(Home, About)</h3>
<pre><code class="language-javascript">import React from &quot;react&quot;;

import { createDrawerNavigator } from &quot;@react-navigation/drawer&quot;;
import { StackNavigator } from &quot;./StackNavigator&quot;;
import About from &#39;./AboutScreen&#39;;


const Drawer = createDrawerNavigator();

const DrawerNavigator = () =&gt; {
  return (
    &lt;Drawer.Navigator
        initialRouteName=&quot;Home&quot;
    &gt;
      &lt;Drawer.Screen name=&quot;Home&quot; component={StackNavigator} options={{drawerLabel: &#39;HOME&#39;}} /&gt;
      &lt;Drawer.Screen name=&quot;About&quot; component={About} options={{drawerLabel: &#39;ABOUT&#39;}} /&gt;
    &lt;/Drawer.Navigator&gt;
  );
}

export default DrawerNavigator;</code></pre>
<p>Drawer에서 접근 가능한 화면들인 Home, About을 정의했다.</p>
<p>이 때, 리스트에서 Home을 클릭 시 HomeScreen으로 바로 연결되는 것이 아니라, Home과 Details 화면을 모두 포함하는 StackNavigator로 component가 설정되어야 한다.</p>
<br>

<h3 id="appjs에서-drawernavigator-호출">App.js에서 DrawerNavigator 호출</h3>
<pre><code class="language-javascript">import React from &#39;react&#39;;

import { NavigationContainer } from &#39;@react-navigation/native&#39;;
import DrawerNavigator from &#39;./DrawerNavigator&#39;;

function App() {
    return (
      &lt;NavigationContainer&gt;
        &lt;DrawerNavigator /&gt;
      &lt;/NavigationContainer&gt;
    );
}

export default App;</code></pre>
<p>최종적으로 App.js에서 DrawerNavigator를 호출하면 끝!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HTML/CSS/JS] 이미지 업로드 - 화면에 파일 이름과 업로드된 이미지 보여주기]]></title>
            <link>https://velog.io/@minkyeong-ko/HTMLCSSJS-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%85%EB%A1%9C%EB%93%9C-%ED%8C%8C%EC%9D%BC%EC%9D%B4%EB%A6%84-%EB%82%98%ED%83%80%EB%82%B4%EA%B8%B0-%ED%99%94%EB%A9%B4%EC%97%90-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%B3%B4%EC%97%AC%EC%A3%BC%EA%B8%B0</link>
            <guid>https://velog.io/@minkyeong-ko/HTMLCSSJS-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%85%EB%A1%9C%EB%93%9C-%ED%8C%8C%EC%9D%BC%EC%9D%B4%EB%A6%84-%EB%82%98%ED%83%80%EB%82%B4%EA%B8%B0-%ED%99%94%EB%A9%B4%EC%97%90-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%B3%B4%EC%97%AC%EC%A3%BC%EA%B8%B0</guid>
            <pubDate>Mon, 17 May 2021 15:08:07 GMT</pubDate>
            <description><![CDATA[<p>사진 출처: pexels, 저작권 고지 필요 없음
<br></p>
<h2 id="구현할-동작은">구현할 동작은?</h2>
<h4 id=""></h4>
<p>버튼을 눌러서 업로드할 이미지 선택 -&gt; 파일 이름 확인하고 제출 -&gt; 화면에 보여지는 이미지 확인</p>
<h4 id="이를-세-가지-과정으로-나눌-수-있다">이를 세 가지 과정으로 나눌 수 있다.</h4>
<blockquote>
<ol>
<li>form과 label로 이미지 업로드 버튼 만들기</li>
<li>javascript로 이미지 파일 처리하기</li>
<li>submit 버튼 클릭 시 이미지 보여주기</li>
</ol>
</blockquote>
<br>

<h2 id="form과-label로-이미지-업로드-버튼-만들기">form과 label로 이미지 업로드 버튼 만들기</h2>
<pre><code class="language-html">&lt;form method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;
    &lt;div class=&quot;button&quot;&gt;
        &lt;label for=&quot;chooseFile&quot;&gt;
            👉 CLICK HERE! 👈
        &lt;/label&gt;
    &lt;/div&gt;
    &lt;input type=&quot;file&quot; id=&quot;chooseFile&quot; name=&quot;chooseFile&quot; accept=&quot;image/*&quot; onchange=&quot;loadFile(this)&quot;&gt;
&lt;/form&gt;</code></pre>
<h4 id="form으로-파일-처리">form으로 파일 처리</h4>
<p>이미지 파일 데이터를 받아야 하기 때문에 enctype을 <em>&quot;multipart/form-data&quot;_로 설정하고 input 태그의 type을 _&quot;file&quot;_로 설정한다.
그리고 input 태그의 accept 속성을 _&quot;image/*&quot;</em> 로 설정하면 이미지 파일만 선택하도록 만들 수 있다.</p>
<h4 id="input-디자인-바꾸기">input 디자인 바꾸기</h4>
<p>이 때, 중요한 점! 
<img src="https://images.velog.io/images/minkyeong-ko/post/a3933a34-554c-44d2-99ec-61e36f650106/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-05-17%20%EC%98%A4%ED%9B%84%2011.58.57.png" alt="">
그냥 이대로 사용하게 되면 디자인이 너무 안예쁘다. 따라서 label을 div로 감싸서 원하는 모양을 만들어주고 input 태그는 숨겼다. </p>
<pre><code class="language-css">/* style.css */

/* label 스타일 조정 */
.button {
    display: flex;
    justify-content: center;
}
label {
    cursor: pointer;
    font-size: 1em;
}

/* 못생긴 기존 input 숨기기 */
#chooseFile {
    visibility: hidden;
}</code></pre>
<p><img src="https://images.velog.io/images/minkyeong-ko/post/76716854-8b2e-4a16-a274-cf3ad15a4648/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-05-17%20%EC%98%A4%ED%9B%84%2011.57.24.png" alt="">
크게 변한 건 없지만 왠지 한결 나아진 기분...</p>
<br>

<h2 id="javascript로-이미지-파일-처리하기">Javascript로 이미지 파일 처리하기</h2>
<pre><code class="language-javascript">function loadFile(input) {
    var file = input.files[0];    //선택된 파일 가져오기

    //미리 만들어 놓은 div에 text(파일 이름) 추가
    var name = document.getElementById(&#39;fileName&#39;);
    name.textContent = file.name;

      //새로운 이미지 div 추가
    var newImage = document.createElement(&quot;img&quot;);
    newImage.setAttribute(&quot;class&quot;, &#39;img&#39;);

    //이미지 source 가져오기
    newImage.src = URL.createObjectURL(file);   

    newImage.style.width = &quot;70%&quot;;
    newImage.style.height = &quot;70%&quot;;
    newImage.style.visibility = &quot;hidden&quot;;   //버튼을 누르기 전까지는 이미지를 숨긴다
    newImage.style.objectFit = &quot;contain&quot;;

    //이미지를 image-show div에 추가
    var container = document.getElementById(&#39;image-show&#39;);
    container.appendChild(newImage);
};</code></pre>
<p>앞선 HTML의 input 태그에서 <em>onchange=&quot;loadFile(this)&quot;</em> 코드를 통해
이미지 파일을 선택하고 완료한 순간, _loadFile_이라는 함수가 실행되게 만들었다.
그리고 이 loadFile 함수에서 파일에 접근해 원하는 동작들을 할 수가 있다.</p>
<h4 id="inputfiles0-filename-urlcreateobjecturlfile">input.files[0], file.name, URL.createObjectURL(file)</h4>
<p>받아온 input 데이터에서 이미지 파일 정보를 가져와서 처리한다. 파일의 이름을 가져와서 HTML에 반영하고, 파일의 url을 가져와서 이미지 div element를 만든다.</p>
<h4 id="newimagestylevisibility--hidden">newImage.style.visibility = &quot;hidden&quot;;</h4>
<p>사용자가 아직 submit 버튼을 누르기 전까지는 이미지가 화면에 보여지면 안되니, visibiltiy를 0으로 설정하였다.</p>
<br>

<h2 id="submit-버튼-클릭-시-이미지-보여주기">submit 버튼 클릭 시 이미지 보여주기</h2>
<p>사용자가 이미지 파일 이름을 확인한 뒤 submit 버튼을 누르게 되면 이미지를 업로드 하는 form 부분을 숨기고 업로드한 이미지만 보여줄 것이다.</p>
<pre><code class="language-javascript">var submit = document.getElementById(&#39;submitButton&#39;);
submit.onclick = showImage;     //Submit 버튼 클릭시 이미지 보여주기

function showImage() {
    var newImage = document.getElementById(&#39;image-show&#39;).lastElementChild;

    //이미지는 화면에 나타나고
    newImage.style.visibility = &quot;visible&quot;;

    //이미지 업로드 버튼은 숨겨진다
    document.getElementById(&#39;image-upload&#39;).style.visibility = &#39;hidden&#39;;

    document.getElementById(&#39;fileName&#39;).textContent = null;     //기존 파일 이름 지우기
}</code></pre>
<p>사실 지금 예시에서는 newImage를 굳이 lastElementChild로 가져올 필요도 없고 기존 파일 이름을 지울 필요도 없지만,
하지만 대부분의 경우에 한번이 아니라 여러번 파일을 가져오는 경우가 많기 때문에 이를 고려하여 작성하였다!</p>
<br>

<h2 id="완성된-전체-코드-보기👀">완성된 전체 코드 보기👀</h2>
<p>!codepen[Mila00a/embed/VwpjNGE?height=627&amp;theme-id=light&amp;default-tab=html,result]</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React Native] 이미지에 CSS filter 효과 적용하기(Blur + Brightness)]]></title>
            <link>https://velog.io/@minkyeong-ko/React-Native-%EC%9D%B4%EB%AF%B8%EC%A7%80%EC%97%90-CSS-filter-%ED%9A%A8%EA%B3%BC-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0Blur-Brightness</link>
            <guid>https://velog.io/@minkyeong-ko/React-Native-%EC%9D%B4%EB%AF%B8%EC%A7%80%EC%97%90-CSS-filter-%ED%9A%A8%EA%B3%BC-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0Blur-Brightness</guid>
            <pubDate>Sat, 15 May 2021 11:25:12 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/minkyeong-ko/post/c0274b43-ea94-4cd0-8489-62d38f1b1d39/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-05-15%20%EC%98%A4%ED%9B%84%208.09.46.png" alt=""></p>
<p>React Native에서는 CSS의 이미지 filter 속성을 사용할 수가 없다.
따라서 ImageBackground의 Style Props를 이용해서 Blur 효과를, 그리고 정석적인 방법은 아니지만 추가적인 View를 통해서 이미지의 밝기를 높이는 효과를 구현할 수 있다.
<br></p>
<h2 id="blur-효과">Blur 효과</h2>
<pre><code class="language-javascript">import React from &#39;react&#39;;
import { View, ImageBackground, } from &#39;react-native&#39;;

function App() {
    return (
      &lt;View style={{flex: 1,}}&gt;
            &lt;ImageBackground 
              source={require(&#39;./test.jpg&#39;)} 
              style={{flex: 1, resizeMode: &#39;cover&#39;,}}
              blurRadius={30}    //Blur 효과
            &gt;
            &lt;/ImageBackground&gt;
      &lt;/View&gt;
    );
}

export default App;</code></pre>
<br>
위처럼 ImageBackground의 blurRadius 속성 값을 조절하여 원하는 블러 효과를 만들어 낼 수 있다. 

<p><br><br></p>
<p>예시로 아래의 원본 사진을 가지고,
<img src="https://images.velog.io/images/minkyeong-ko/post/af169e4f-43ac-4de1-a3ad-4bd4516438f0/Simulator%20Screen%20Shot%20-%20iPhone%2012%20Pro%20-%202021-05-15%20at%2018.35.27.png" alt=""></p>
<br>

<p>blurRadius를 10, 50, 30 으로 설정한 결과는 아래와 같다.
<img src="https://images.velog.io/images/minkyeong-ko/post/10ec0852-b674-4b1f-8424-435236e8db6b/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-05-15%20%EC%98%A4%ED%9B%84%208.09.46.png" alt="">
<br></p>
<h2 id="brightness-효과">Brightness 효과</h2>
<pre><code class="language-javascript">import React from &#39;react&#39;;
import { View, ImageBackground, } from &#39;react-native&#39;;

function App() {
    return (
      &lt;View style={{flex: 1,}}&gt;
            &lt;ImageBackground 
              source={require(&#39;./test.jpg&#39;)} 
              style={{flex: 1, resizeMode: &#39;cover&#39;,}}
              blurRadius={30}
            &gt;
                //View를 추가하여 밝기 조절하기
                &lt;View style={{flex: 1, backgroundColor: &#39;rgba(255, 255, 255, 0.3)&#39;,}}&gt;&lt;/View&gt;

            &lt;/ImageBackground&gt;
      &lt;/View&gt;
    );
}

export default App;</code></pre>
<br>

<p>Blur 효과를 적용한 상태에서 View를 추가했다.</p>
<br>

<p>이미지를 꽉 채우는 View를 만들고, 배경을 반투명한 흰색으로 설정해 이미지가 밝아보이게 만들었다.
<img src="https://images.velog.io/images/minkyeong-ko/post/c7eef8ed-4924-4d45-b2ee-d82036e66d16/Simulator%20Screen%20Shot%20-%20iPhone%2012%20Pro%20-%202021-05-15%20at%2018.40.01.png" alt=""></p>
<br>

<p>반대로 어둡게 만드려면 backgroundColor를 &#39;rgba(0, 0, 0, 0.3)&#39; 으로 (투명도는 자유롭게) 설정하면 된다.
<img src="https://images.velog.io/images/minkyeong-ko/post/06cc6691-7bf4-4c8f-9ea9-ba9d8d3cacde/Simulator%20Screen%20Shot%20-%20iPhone%2012%20Pro%20-%202021-05-15%20at%2018.40.30.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>