<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Nayoung Gu - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/korean/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Nayoung Gu - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/korean/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 19:36:49 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/korean/news/author/gunayoung/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 자바스크립트 프로미스 튜토리얼 - 자바스크립트의 프로미스를 이행하거나 거부하는 방법 ]]>
                </title>
                <description>
                    <![CDATA[ 프로미스는 자바스크립트에서 비동기 처리를 위한 중요한 요소이지만 프로미스를 이해하고 적용하는 것은 그렇게 쉽지 않다고 생각하셨을 수도 있습니다. 하지만 절 따라오세요, 여러분은 혼자가 아닙니다! 프로미스는 프로미스를 몇 년 동안 다뤄본 많은 웹 개발자들에게도 어려운 주제입니다. 이 글에서는 제가 지난 몇 년간 자바스크립트의 프로미스에 대해 배운 내용을 공유하면서 이러한 인식을 한번 깨보겠습니다. ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/javascript-promise-tutorial-how-to-resolve-or-reject-promises-in-js/</link>
                <guid isPermaLink="false">65336269722f6d03ea1726b0</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nayoung Gu ]]>
                </dc:creator>
                <pubDate>Sat, 21 Oct 2023 10:29:46 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2023/10/cover-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/javascript-promise-tutorial-how-to-resolve-or-reject-promises-in-js/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">JavaScript Promise Tutorial – How to Resolve or Reject Promises in JS</a>
      </p><!--kg-card-begin: markdown--><h4 id=""><code>프로미스</code>는 자바스크립트에서 비동기 처리를 위한 중요한 요소이지만 프로미스를 이해하고 적용하는 것은 그렇게 쉽지 않다고 생각하셨을 수도 있습니다. 하지만 절 따라오세요, 여러분은 혼자가 아닙니다!</h4>
<p>프로미스는 프로미스를 몇 년 동안 다뤄본 많은 웹 개발자들에게도 어려운 주제입니다.</p>
<p>이 글에서는 제가 지난 몇 년간 자바스크립트의 프로미스에 대해 배운 내용을 공유하면서 이러한 인식을 한번 깨보겠습니다. 도움이 되었으면 좋겠습니다.</p>
<h2 id="">자바스크립트의 프로미스란</h2>
<p><code>프로미스</code>는 자바스크립트의 특수 객체입니다. 프로미스는 <code>비동기</code> 작업이 성공적으로 수행되면 값을 생성하고, 시간 초과나 네트워크 오류 등으로 인해 실패하면 에러를 생성합니다.</p>
<p>성공적인 이행은 <code>resolve</code> 함수 호출, 그리고 에러는 <code>reject</code> 함수 호출을 통해 확인할 수 있습니다.</p>
<p>프로미스는 다음과 같이 생성자를 통해 만들 수 있습니다.</p>
<pre><code class="language-js">let promise = new Promise(function(resolve, reject) {    
    // 비동기 호출 후 resolve 혹은 reject 하는 곳
});
</code></pre>
<p>대부분의 경우 프로미스는 비동기 처리에 사용됩니다. 하지만 엄밀히 말하면 동기와 비동기 작업을 모두 이행/거절할 수 있습니다.</p>
<h2 id="">잠시만요, 비동기 작업을 위한 <code>콜백</code> 함수가 있지 않나요?</h2>
<p>네! 맞아요. 자바스크립트에는 <code>콜백</code> 함수가 있습니다. 하지만 콜백은 자바스크립트에서 특별한 개념은 아닙니다. <code>비동기</code> 호출이 (성공 또는 에러와 함께) 완료되면 결과를 반환하는 일반 함수입니다.</p>
<p><code>비동기</code>란 지금 당장이 아니라 미래에 발생한다는 의미입니다. 콜백은 주로 네트워크 통신, 업로드/다운로드, 데이터베이스 조회 등과 같은 상황에서 사용됩니다.</p>
<p><code>콜백</code>은 유용하긴 하지만 엄청난 단점도 가지고 있습니다. 때로 다른 콜백에 포함된 콜백 내부에 또 다른 콜백이 있을 수도 있습니다. 진심으로요! 이 "콜백 헬"을 예시와 함께 이해해봅시다.</p>
<h3 id="pizzahub">콜백 헬을 피하는 방법 - PizzaHub 예시</h3>
<p>PizzaHub에서 🍕 야채 마르게리타 피자를 주문한다고 해봅시다. 주문을 넣으면 PizzaHub은 자동적으로 우리의 위치를 파악하고 근처의 피자 가게를 찾은 뒤 우리가 요청한 피자가 주문 가능한지 찾아볼 것입니다.</p>
<p>주문이 가능하다면 피자와 함께 무료로 받을 수 있는 음료를 파악하고 마침내 주문을 넣게 됩니다.</p>
<p>주문이 성공적으로 완료되면 확인 문자를 받게 됩니다.</p>
<p>이 과정을 콜백 함수를 사용해 코드로 표현하면 어떨까요? 다음과 같이 생각해 봤습니다.</p>
<pre><code class="language-js">function orderPizza(type, name) {

    // pizzahub에 문의하기
    query(`/api/pizzahub/`, function(result, error){
       if (!error) {
           let shopId = result.shopId;

           // 가게에 피자 문의하기
           query(`/api/pizzahub/pizza/${shopid}`, function(result, error){
               if (!error) {
                   let pizzas = result.pizzas;

                   // 피자 가능 여부 확인
                   let myPizza = pizzas.find((pizza) =&gt; {
                       return (pizza.type===type &amp;&amp; pizza.name===name);
                   });

                   // 무료 음료 확인
                   query(`/api/pizzahub/beverages/${myPizza.id}`, function(result, error){
                       if (!error) {
                           let beverage = result.id;

                           // 주문 준비
                           query(`/api/order`, {'type': type, 'name': name, 'beverage': beverage}, function(result, error){
                              if (!error) {
                                  console.log(`Your order of ${type} ${name} with ${beverage} has been placed`);
                              } else {
                                  console.log(`Bad luck, No Pizza for you today!`);
                              }
                           });

                       }
                   })
               }
           });
       } 
    });
}

// orderPizza 메서드 호출
orderPizza('veg', 'margherita');
</code></pre>
<p>위의 <code>orderPizza</code> 함수를 잘 살펴봅시다.</p>
<p>이 함수는 근처 피자 가게의 id를 찾기 위해 API를 호출합니다. 그리고 그 가게에서 주문 가능한 피자의 리스트를 받습니다. 그 중에 주문한 피자가 있는지 확인하고 그 피자와 함께 오는 음료를 확인하기 위해 또 다른 API 호출을 하게 됩니다. 최종적으로 주문 API를 통해 주문을 넣게 됩니다.</p>
<p>여기서 모든 API에 콜백을 사용하고 있습니다. 이 때문에 계속해서 콜백 안에 콜백을 넣는 방식으로 코드를 작성하게 됩니다.</p>
<p>이렇게 해서 우리는 <code>콜백 지옥</code>에 빠지게 됩니다. 누가 이런 코드를 원할까요? 게다가 가독성이 좋지 않고 에러가 발생하기도 쉬운 코드 피라미드가 생겨나게 됩니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/callback-hell.png" alt="콜백 지옥의 예시 모양" width="600" height="400" loading="lazy"></p>
<p align="center">콜백 지옥 및 멸망의 피라미드 예시</p>
<p><code>콜백 지옥</code>을 빠져나오거나 들어가지 않을 수 있는 몇 가지 방법들이 있습니다. 가장 흔한 방법은 <code>프로미스</code>나 <code>비동기</code> 함수를 사용하는 것입니다. 하지만 <code>async</code> 함수를 잘 이해하려면 <code>프로미스</code>를 먼저 잘 이해해야 합니다.</p>
<p>그럼 프로미스에 대해서 시작해 봅시다.</p>
<h2 id="">프로미스의 상태 이해하기</h2>
<p>복습해보자면, 프로미스는 다음과 같은 생성자 문법을 통해 생성될 수 있습니다.</p>
<pre><code class="language-js">let promise = new Promise(function(resolve, reject) {
  // 실행할 함수
});
</code></pre>
<p>생성자 함수는 인자로 함수를 받습니다. 이 함수는 <code>실행 함수</code>라고 불립니다.</p>
<pre><code class="language-js">// 실행 함수는 프로미스 생성자 함수에 인자로 전달된다
function(resolve, reject) {
    // 로직이 위치할 곳
}
</code></pre>
<p>실행 함수는 <code>resolve</code>와 <code>reject</code>라는 두 가지 인자를 받습니다. 이 둘은 자바스크립트에 의해 제공되는 콜백 함수입니다. 여러분이 작성할 로직은 <code>new Promise</code>가 생성되면 자동적으로 호출될 실행 함수 안에 위치하게 됩니다.</p>
<p>프로미스를 효과적으로 사용하려면 실행 함수는 <code>resolve</code> 혹은 <code>reject</code> 콜백 함수들 중 하나를 호출해야 합니다. 이 둘에 대해선 잠시 후에 더 자세히 배워보겠습니다.</p>
<p><code>new Promise()</code> 생성자는 <code>프로미스</code> 객체를 반환합니다. 실행 함수는 비동기 처리를 다루기 때문에, 반환된 프로미스 객체는 작업이 시작되거나, 완료될 때 (resolved), 그리고 에러를 반환할 때(rejected)를 알려줄 수 있어야 합니다.</p>
<p><code>프로미스</code> 객체는 다음과 같은 내부 프로퍼티를 갖습니다.</p>
<ol>
<li><code>state(상태)</code> - 이 프로퍼티는 다음과 같은 값을 갖습니다.</li>
</ol>
<ul>
<li><code>pending(대기)</code>: 실행 함수가 작업을 시작했을 때</li>
<li><code>fulfilled(이행)</code>: 프로미스가 이행되었을 때</li>
<li><code>rejected(거부)</code>: 프로미스가 거부되었을 때</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/states_1.png" alt="프로미스의 상태" width="600" height="400" loading="lazy"></p>
<p align="center">프로미스의 세 가지 상태</p>
<ol start="2">
<li><code>result(결과)</code> - 이 프로퍼티는 다음과 같은 값을 갖습니다.</li>
</ol>
<ul>
<li><code>undefined</code>: <code>상태</code> 값이 <code>대기</code> 중일 때</li>
<li><code>value</code>: <code>resolve(value)</code>가 호출되었을 때</li>
<li><code>error</code>: <code>reject(error)</code>가 호출되었을 때</li>
</ul>
<p>이 내부 프로퍼티들은 코드로 접근할 수는 없지만 확인은 가능합니다. 즉 디버거 도구를 활용해 <code>state</code>와 <code>result</code> 프로퍼티 값을 확인할 수 있지만 직접적으로 프로그램을 사용해 접근할 수는 없습니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/promise_state_inspect.png" alt="개발자 도구에서 프로미스 확인하기" width="600" height="400" loading="lazy"></p>
<p align="center">프로미스의 내부 프로머티를 확인할 수 있다</p>
<p>프로미스의 상태는 <code>pending</code>, <code>fulfilled</code> 혹은 <code>rejected</code>가 될 수 있습니다. 이행(resolved) 또는 거부(rejected)된 프로미스는 <code>settled</code> 되었다고 표현합니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/states_2.png" alt="프로미스가 처리된 상태" width="600" height="400" loading="lazy"></p>
<p align="center">처리(settled)된 프로미스는 이행(fulfilled) 혹은 거부(rejected)된 것이다</p>
<h3 id="">프로미스가 이행되거나 거부되는 방법</h3>
<p>다음은 <code>I am done</code>이라는 값과 함께 즉시 이행되는(<code>fulfilled</code> 상태) 프로미스의 예시입니다.</p>
<pre><code class="language-js">let promise = new Promise(function(resolve, reject) {
    resolve("I am done");
});
</code></pre>
<p>다음은 <code>Something is not right!</code>이라는 에러 메세지와 함께 거절되는 (<code>rejected</code> 상태) 프로미스입니다.</p>
<pre><code class="language-js">let promise = new Promise(function(resolve, reject) {
    reject(new Error('Something is not right!'));
});
</code></pre>
<p>주의할 점</p>
<blockquote>
<p>프로미스 실행 함수는 오직 하나의 <code>resolve</code> 혹은 <code>reject</code> 함수를 호출해야 합니다. 상태가 한번 변경되면 (pending =&gt; fulfilled 혹은 pending =&gt; rejected) 끝이기 때문입니다. 그 이후의 <code>resolve</code> 혹은 <code>reject</code>는 무시됩니다.</p>
</blockquote>
<pre><code class="language-js">let promise = new Promise(function(resolve, reject) {
  resolve("I am surely going to get resolved!");

  reject(new Error('Will this be ignored?')); // 무시됨
  resolve("Ignored?"); // 무시됨
});
</code></pre>
<p>위의 예시에서, 오직 첫 번째 것만 이행되고 나머지 것들은 모두 무시됩니다.</p>
<h2 id="">프로미스를 만들고 처리하는 방법</h2>
<p><code>프로미스</code>는 (대부분 비동기적으로) 작업을 처리하기 위해 실행 함수를 사용합니다. (프로미스의 결과를 사용하는) 소비 함수는 실행 함수가 이행(성공)되거나 거부된(에러) 시점을 알아야 합니다.</p>
<p><code>.then()</code>, <code>.catch()</code>, <code>finally()</code>와 같은 핸들러 메서드는 실행 함수와 소비 함수를 이어줌으로써 프로미스가 <code>이행</code>되거나 <code>거부</code>되었을 때 서로 동기화될 수 있도록 해줍니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/consumer_executor.png" alt="실행 함수와 소비 함수 예시" width="600" height="400" loading="lazy"></p>
<p align="center">실행 함수와 소비 함수</p>
<h3 id="then"><code>.then()</code> 메서드를 사용하는 방법</h3>
<p><code>.then()</code> 메서드는 결과(resolve) 또는 에러(reject)를 다룰 때 프로미스 객체에 의해 호출됩니다.</p>
<p>이 메서드는 인자로 두 개의 함수를 받습니다. 보통 <code>.then()</code> 메서드는 프로미스의 실행 결과를 알고자 하는 소비 함수로부터 실행되어야 합니다.</p>
<pre><code class="language-js">promise.then(
  (result) =&gt; { 
     console.log(result);
  },
  (error) =&gt; { 
     console.log(error);
  }
);
</code></pre>
<p>성공했을 때의 결과만 알고 싶다면 다음과 같이 하나의 인자만 전달해줄 수도 있습니다.</p>
<pre><code class="language-js">promise.then(
  (result) =&gt; { 
      console.log(result);
  }
);
</code></pre>
<p>실패한 결과만 다루고 싶다면 다음과 같이 첫 번째 인자로 <code>null</code>을 전달해주면 됩니다.</p>
<pre><code class="language-js">promise.then(
  null,
  (error) =&gt; { 
      console.log(error)
  }
);
</code></pre>
<p>하지만 잠시후에 보게될 <code>.catch()</code> 메서드를 사용하면 더 나은 방법으로 에러를 처리할 수 있습니다.</p>
<p><code>.then()</code>과 <code>.catch()</code> 핸들러를 사용해서 결과와 에러를 처리한 몇가지 예시를 살펴봅시다. 몇 가지 실제 비동기 요청을 통해서 더 재밌게 배워보겠습니다. 포켓몬에 대한 정보를 얻고 그 결과를 프로미스를 사용해서 이행/거부하기 위해 <a href="https://pokeapi.co/">PokeAPI</a>를 사용하겠습니다.</p>
<p>먼저, PokeAPI URL을 인자로 받은 뒤 프로미스를 반환하기 위한 제네릭 함수를 만듭니다. API 호출이 성공적으로 이루어지면 이행된 프로미스가 반환됩니다. 거부된 프로미스는 어떠한 종류의 에러로부터 반환될 수 있습니다.</p>
<p>이제부터 프로미스를 만들어 작업하기 위해 여러 예시에서 이 함수를 사용하겠습니다.</p>
<pre><code class="language-js">function getPromise(URL) {
  let promise = new Promise(function (resolve, reject) {
    let req = new XMLHttpRequest();
    req.open("GET", URL);
    req.onload = function () {
      if (req.status == 200) {
        resolve(req.response);
      } else {
        reject("There is an Error!");
      }
    };
    req.send();
  });
  return promise;
}
</code></pre>
<p align="center">프로미스를 위한 유틸리티 메서드</p>
<p>예시 1: 50가지의 포켓몬 정보 얻기</p>
<pre><code class="language-js">const ALL_POKEMONS_URL = 'https://pokeapi.co/api/v2/pokemon?limit=50';

// 아래 함수는 위에서 언급했었죠!
let promise = getPromise(ALL_POKEMONS_URL);

const consumer = () =&gt; {
    promise.then(
        (result) =&gt; {
            console.log({result}); // 50 가지의 포켓몬 결과 출력
        },
        (error) =&gt; {
            // 유효한 URL이라면 실행되지 않는 부분
            console.log('We have encountered an Error!'); // 에러 출력
    });
}

consumer();
</code></pre>
<p>예시 2: 유효하지 않은 URL일 때</p>
<pre><code class="language-js">const POKEMONS_BAD_URL = 'https://pokeapi.co/api/v2/pokemon-bad/';

// 아래 코드는 URL의 404 에러로 인해 거절됩니다
let promise = getPromise(POKEMONS_BAD_URL);

const consumer = () =&gt; {
    promise.then(
        (result) =&gt; {
            // 프로미스는 이행(resolve)되지 않았기 때문에 이 부분은 실행되지 않습니다.
            console.log({result});
        },
        (error) =&gt; {
            // 거절된(rejected) 프로미스는 이 부분을 실행할 것입니다.
            console.log('We have encountered an Error!'); // 에러 출력
        }
    );
}

consumer();
</code></pre>
<h3 id="catch"><code>.catch()</code> 메서드를 사용하는 방법</h3>
<p>프로미스의 에러(거부)를 다룰 때 이 메서드를 사용할 수 있습니다. <code>.then()</code>의 첫 번째 인자로 <code>null</code>을 전달해주는 문법은 에러를 다루기 좋은 방법은 아닙니다. 그래서 같은 작업을 위해 <code>.catch()</code>를 사용해 더 깔끔한 문법으로 작성할 수 있습니다.</p>
<pre><code class="language-js">// 유효하지 않은 URL(404 에러)로 인해 거부될 프로미스
let promise = getPromise(POKEMONS_BAD_URL);

const consumer = () =&gt; {
    promise.catch(error =&gt; console.log(error));
}

consumer();
</code></pre>
<p>프로미스 실행 함수나 핸들러에서 <code>reject</code> 함수를 실행하는 대신 <code>new Error("Something wrong!")</code>과 같은 에러를 던지게 되면 이 프로미스는 거부된 것으로 해석될 것입니다. 즉 <code>.catch</code> 핸들러 메서드에 의해 에러가 잡히게 됩니다.</p>
<p>프로미스 실행 함수와 핸들러 함수에서 일어나는 <em>동기적인</em> 예외 상황에서도 같게 동작합니다.</p>
<p>다음은 프로미스가 거부된 것으로 처리되어 <code>.catch</code> 핸들러 메서드가 호출된 예시입니다.</p>
<pre><code class="language-js">new Promise((resolve, reject) =&gt; {
  throw new Error("Something is wrong!");// reject 함수를 실행하지 않습니다
}).catch((error) =&gt; console.log(error)); 
</code></pre>
<h3 id="finally"><code>.finally()</code> 메서드를 사용하는 방법</h3>
<p><code>.finally()</code> 핸들러는 로더를 멈추거나 실시간 연결을 끊는 등 정리를 위해 사용됩니다. <code>finally</code> 메서드는 프로미스의 <code>성공</code>이나 <code>거부</code> 여부와 상관 없이 실행됩니다. 이 메서드는 .then()이나 .catch()를 또 실행할 수 있는 다음 핸들러에게 결과 값이나 에러를 전달합니다.</p>
<p>세 메서드를 함께 이해할 수 있는 예시를 살펴보겠습니다.</p>
<pre><code class="language-js">let loading = true;
loading &amp;&amp; console.log('Loading...');

// 프로미스 얻기
promise = getPromise(ALL_POKEMONS_URL);

promise.finally(() =&gt; {
    loading = false;
    console.log(`Promise Settled and loading is ${loading}`);
}).then((result) =&gt; {
    console.log({result});
}).catch((error) =&gt; {
    console.log(error)
});
</code></pre>
<p>추가 설명</p>
<ul>
<li><code>.finally()</code> 메서드는 로딩을 <code>false</code>로 설정합니다.</li>
<li>프로미스가 이행되면 <code>.then()</code> 메서드가 실행됩니다. 프로미스가 에러와 함께 거부되면 <code>.catch()</code> 메서드가 실행됩니다. <code>.finally()</code>는 이행 혹은 거부와는 관계 없이 실행됩니다.</li>
</ul>
<h2 id="">프로미스 체인이란</h2>
<p><code>promise.then()</code>은 항상 프로미스를 반환합니다. 이 프로미스는 <code>pending</code>이라는 <code>상태</code>와 <code>undefined</code>라는 <code>결과</code>를 갖게될 것입니다. 이를 통해 다음 프로미스에 <code>.then</code> 메서드를 호출할 수 있게 해줍니다.</p>
<p>첫 번째 <code>.then</code> 메서드가 반환한 결과 값은 다음 <code>.then</code> 메서드가 받을 수 있습니다. 두 번째 결과는 이제 세 번째 <code>.then()</code>으로 전달될 수 있고 이런 방식으로 계속 이어질 수 있습니다. 이 방식은 프로미스를 밑으로 전달해주는 <code>.then</code> 메서드 체인을 형성합니다. 이 현상은 <code>프로미스 체인</code>이라고 불립니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-105.png" alt="프로미스 체인" width="600" height="400" loading="lazy"></p>
<p align="center">프로미스 체인</p>
<p>다음 예제를 봅시다.</p>
<pre><code class="language-js">let promise = getPromise(ALL_POKEMONS_URL);

promise.then(result =&gt; {
    let onePokemon = JSON.parse(result).results[0].url;
    return onePokemon;
}).then(onePokemonURL =&gt; {
    console.log(onePokemonURL);
}).catch(error =&gt; {
    console.log('In the catch', error);
});
</code></pre>
<p>먼저 프로미스를 이행하고 첫번째 포켓몬을 얻기 위한 URL을 추출합니다. 그렇게 얻은 결과는 프로미스 형태로 다음 .then() 핸들러로 전달됩니다. 결과는 다음과 같습니다.</p>
<pre><code class="language-js">https://pokeapi.co/api/v2/pokemon/1/
</code></pre>
<p><code>.then</code> 메서드는 다음 중 하나를 반환합니다.</p>
<ul>
<li>결과 (이미 살펴본 것이죠)</li>
<li>새로운 프로미스</li>
</ul>
<p>에러를 반환할 수도 있습니다.</p>
<p>다음 예시는 결과와 새로운 프로미스를 반환하는 <code>.then</code> 메서드로 이루어진 프로미스 체인을 만든 것입니다.</p>
<pre><code class="language-js">// 다수의 then과 catch로 이루어진 프로미스 체인
let promise = getPromise(ALL_POKEMONS_URL);

promise.then(result =&gt; {
    let onePokemon = JSON.parse(result).results[0].url;
    return onePokemon;
}).then(onePokemonURL =&gt; {
    console.log(onePokemonURL);
    return getPromise(onePokemonURL);
}).then(pokemon =&gt; {
    console.log(JSON.parse(pokemon));
}).catch(error =&gt; {
    console.log('In the catch', error);
});
</code></pre>
<p>첫 번째 <code>.then()</code>은 추출한 URL을 결과로 반환합니다. 이 URL은 해당 URL을 인자로 받는 새로운 프로미스를 반환하는 두 번째 <code>.then</code>으로 전달됩니다.</p>
<p>이 프로미스가 이행되면 포켓몬에 대한 정보 얻을 수 있는 체인으로 전달됩니다. 결과는 다음과 같습니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/image-159.png" alt="콘솔로 확인한 프로미스 체인의 결과" width="600" height="400" loading="lazy"></p>
<p align="center">프로미스 체인의 결과</p>
<p>에러나 프로미스 거부가 발생하면 프로미스 체인의 .catch 메서드가 실행됩니다.</p>
<p>중요한 점: <code>.then</code>을 여러번 호출한다고 프로미스 체인이 생성되는 것은 아닙니다. 다음과 같이 코드를 작성하면 버그만 발생할 것입니다.</p>
<pre><code class="language-js">let promise = getPromise(ALL_POKEMONS_URL);

promise.then(result =&gt; {
    let onePokemon = JSON.parse(result).results[0].url;
    return onePokemon;
});
promise.then(onePokemonURL =&gt; {
    console.log(onePokemonURL);
    return getPromise(onePokemonURL);
});
promise.then(pokemon =&gt; {
    console.log(JSON.parse(pokemon));
});
</code></pre>
<p>같은 프로미스에 <code>.then</code> 메서드를 세 번 호출하고 있지만 프로미스를 전달하고 있지는 않습니다. 이것은 프로미스 체인과는 다릅니다. 위 예시의 결과로 에러가 나올 것입니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/image-160.png" alt="프로미스 체인을 잘못 사용했을 때 나타나는 에러" width="600" height="400" loading="lazy"></p>
<h2 id="">여러 개의 프로미스를 처리하는 방법</h2>
<p>.then, .catch, .finally 같은 핸들러 메서드와는 별개로 프로미스 API에서 사용 가능한 여섯 가지의 정적 메서드가 있습니다. 첫 네 개의 메서드들은 프로미스 배열을 받아 병렬적으로 실행합니다.</p>
<ol>
<li>Promise.all</li>
<li>Promise.any</li>
<li>Promise.allSettled</li>
<li>Promise.race</li>
<li>Promise.resolve</li>
<li>Promise.reject</li>
</ol>
<p>하나씩 살펴봅시다.</p>
<h3 id="promiseall">Promise.all() 메서드</h3>
<p><code>Promise.all([promises])</code>는 인자로 프로미스의 집합(예를 들면, 배열)을 받고 병렬로 실행시킵니다.</p>
<p>이 메서드는 모든 프로미스가 이행(resolve)되기를 기다린 후에 프로미스의 결과를 배열로 반환합니다. 만약 프로미스 중 어떤 하나라도 거절(reject)되거나 에러로 인해 실패한다면 다른 모든 프로미스의 결과는 무시될 것입니다.</p>
<p>세 마리의 포켓몬에 대한 정보를 얻기 위해 세 개의 프로미스를 생성해봅시다.</p>
<pre><code class="language-js">const BULBASAUR_POKEMONS_URL = 'https://pokeapi.co/api/v2/pokemon/bulbasaur';
const RATICATE_POKEMONS_URL = 'https://pokeapi.co/api/v2/pokemon/raticate';
const KAKUNA_POKEMONS_URL = 'https://pokeapi.co/api/v2/pokemon/kakuna';

let promise_1 = getPromise(BULBASAUR_POKEMONS_URL);
let promise_2 = getPromise(RATICATE_POKEMONS_URL);
let promise_3 = getPromise(KAKUNA_POKEMONS_URL);
</code></pre>
<p>Promise.all() 메서드에 프로미스 배열을 전달해 사용합니다.</p>
<pre><code class="language-js">Promise.all([promise_1, promise_2, promise_3]).then(result =&gt; {
    console.log({result});
}).catch(error =&gt; {
    console.log('에러 발생');
});
</code></pre>
<p>결과:<br>
<img src="https://www.freecodecamp.org/news/content/images/2020/11/image-161.png" alt="Promise.all() 메서드의 처리 결과" width="600" height="400" loading="lazy"></p>
<p>결과에서 볼 수 있듯, 모든 프로미스의 결과가 반환됩니다. 모든 프로미스를 실행시키는 시간은 프로미스가 실행되는 데 걸리는 최대 시간과 같습니다.</p>
<h3 id="promiseany">Promise.any() 메서드</h3>
<p><code>all()</code> 메서드와 비슷한 <code>Promise.any([promises])</code> 또한 병렬로 처리할 프로미스를 배열로 받습니다. 이 메서드는 모든 프로미스가 이행(resolve)되기를 기다리지 않습니다. 프로미스 중 하나라도 settled 되면 이행이 종료됩니다.</p>
<pre><code class="language-js"> Promise.any([promise_1, promise_2, promise_3]).then(result =&gt; {
     console.log(JSON.parse(result));
 }).catch(error =&gt; {
     console.log('에러 발생');
 });
</code></pre>
<p>결과는 이행(resolve)된 프로미스 중 하나가 될 것입니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/image-162.png" alt="프로미스가 하나라도 이행된 경우의 콘솔 결과" width="600" height="400" loading="lazy"></p>
<h3 id="promiseallsettled">Promise.allSettled() 메서드</h3>
<p><code>Promise.allSettled([promises])</code> 메서드는 모든 프로미스가 settled(resolve/reject) 되기를 기다린 후에 객체로 구성된 배열을 결과로 반환합니다. 성공적으로 이행된다면 결과는 상태(fulfilled/rejected)와 결괏값을 포함할 것입니다. 거절된(rejected) 상태라면 에러에 대한 이유를 반환할 것입니다.</p>
<p>이행된 프로미스들의 예시를 살펴보겠습니다.</p>
<pre><code class="language-js">Promise.allSettled([promise_1, promise_2, promise_3]).then(result =&gt; {
    console.log({result});
}).catch(error =&gt; {
    console.log('에러입니다!');
});
</code></pre>
<p>결과:<br>
<img src="https://www.freecodecamp.org/news/content/images/2020/11/image-163.png" alt="모든 프로미스가 이행된 경우의 콘솔 결과" width="600" height="400" loading="lazy"></p>
<p>프로미스 중 하나인 promise_1이 거부된다면 다음과 같은 결과가 나옵니다.</p>
<pre><code class="language-js">let promise_1 = getPromise(POKEMONS_BAD_URL);
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/image-164.png" alt="promise_1만 거부된 경우의 콘솔 결과" width="600" height="400" loading="lazy"></p>
<h3 id="promiserace">Promise.race() 메서드</h3>
<p><code>Promise.race([promises])</code>는 (가장 빠른) 첫번째 프로미스가 settled 되기를 기다린 후에 그에 따른 결과/에러를 반환합니다.</p>
<pre><code class="language-js">Promise.race([promise_1, promise_2, promise_3]).then(result =&gt; {
    console.log(JSON.parse(result));
}).catch(error =&gt; {
    console.log('에러 발생');
});
</code></pre>
<p>이행된(resolved) 가장 빠른 프로미스의 결과는 다음과 같습니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/image-165.png" alt="race 메서드의 콘솔 출력 결과" width="600" height="400" loading="lazy"></p>
<h3 id="promiseresolvereject">Promise.resolve/reject 메서드</h3>
<p><code>Promise.resolve(value)</code>는 전달된 값과 함께 프로미스를 이행합니다. 다음과 같습니다.</p>
<pre><code class="language-js">let promise = new Promise(resolve =&gt; resolve(value));
</code></pre>
<p><code>Promise.reject(error)</code>은 다음과 같이 전달된 에러와 함께 프로미스를 거부합니다.</p>
<pre><code class="language-js">let promise = new Promise((resolve, reject) =&gt; reject(error));
</code></pre>
<h2 id="pizzahub">PizzaHub 예시를 프로미스로 바꿀 수 있나요?</h2>
<p>그럼요, 한번 해봅시다. <code>query</code> 메서드가 프로미스를 반환한다고 가정해봅시다. 여기 query() 메서드의 예시가 있습니다. 실제로 이 메서드는 데이터베이스와 소통해서 결과를 반환합니다. 지금 경우에는 하드코딩되어 있지만 같은 역할을 수행합니다.</p>
<pre><code class="language-js">function query(endpoint) {
  if (endpoint === `/api/pizzahub/`) {
    return new Promise((resolve, reject) =&gt; {
      resolve({'shopId': '123'});
    })
  } else if (endpoint.indexOf('/api/pizzahub/pizza/') &gt;=0) {
    return new Promise((resolve, reject) =&gt; {
      resolve({pizzas: [{'type': 'veg', 'name': 'margherita', 'id': '123'}]});
    })
  } else if (endpoint.indexOf('/api/pizzahub/beverages') &gt;=0) {
    return new Promise((resolve, reject) =&gt; {
      resolve({id: '10', 'type': 'veg', 'name': 'margherita', 'beverage': 'coke'});
    })
  } else if (endpoint === `/api/order`) {
    return new Promise((resolve, reject) =&gt; {
      resolve({'type': 'veg', 'name': 'margherita', 'beverage': 'coke'});
    })
  }
}
</code></pre>
<p>다음은 <code>콜백 지옥</code>을 리팩토링 해보겠습니다. 이를 위해 먼저 몇개의 논리적인 함수를 만들겠습니다.</p>
<pre><code class="language-js">// 가게 id 반환
let getShopId = result =&gt; result.shopId;

// 가게의 피자 리스트가 담긴 프로미스 반환
let getPizzaList = shopId =&gt; {
  const url = `/api/pizzahub/pizza/${shopId}`;
  return query(url);
}

// 고객의 요청에 맞는 피자를 프로미스로 반환
let getMyPizza = (result, type, name) =&gt; {
  let pizzas = result.pizzas;
  let myPizza = pizzas.find((pizza) =&gt; {
    return (pizza.type===type &amp;&amp; pizza.name===name);
  });
  const url = `/api/pizzahub/beverages/${myPizza.id}`;
  return query(url);
}

// 주문 후 프로미스 반환
let performOrder = result =&gt; {
  let beverage = result.id;
   return query(`/api/order`, {'type': result.type, 'name': result.name, 'beverage': result.beverage});
}

// 주문 확인
let confirmOrder = result =&gt; {
    console.log(`Your order of ${result.type} ${result.name} with ${result.beverage} has been placed!`);
}
</code></pre>
<p>필요한 프로미스를 만들기 위해 위 함수들을 사용합니다. 여기서 <code>콜백 지옥</code> 예제와 비교해볼 수 있습니다. 아래 코드는 확실히 깔끔하고 우아하네요.</p>
<pre><code class="language-js">function orderPizza(type, name) {
  query(`/api/pizzahub/`)
  .then(result =&gt; getShopId(result))
  .then(shopId =&gt; getPizzaList(shopId))
  .then(result =&gt; getMyPizza(result, type, name))
  .then(result =&gt; performOrder(result))
  .then(result =&gt; confirmOrder(result))
  .catch(function(error){
    console.log(`Bad luck, No Pizza for you today!`);
  })
}
</code></pre>
<p>마지막으로 다음과 같이 피자 종류와 이름을 전달해서 orderPizza() 메서드를 호출합니다.</p>
<pre><code class="language-js">orderPizza('veg', 'margherita');
</code></pre>
<h2 id="">다음으로 배울 내용</h2>
<p>여기까지 거의 읽으셨다면 축하드립니다! 이제 여러분은 자바스크립트의 프로미스에 대해 더 잘 이해할 수 있으실 겁니다. 이 글에서 사용된 모든 예제는 이 <a href="https://github.com/atapas/js-promise-example">깃허브 레포지토리</a>에 있습니다.</p>
<p>다음으로는 더 간단하게 코드를 작성할 수 있는 자바스크립트의 <code>async</code> 함수를 배울 차례입니다. 자바스크립트의 프로미스를 학습할 수 있는 가장 좋은 방법은 작은 예제를 만들고 쌓아가는 것입니다.</p>
<p>Angular, React, Vue 등과 같은 프레임워크나 라이브러리 사용과는 관계 없이 비동기 처리는 필수적입니다. 즉, 일을 더 잘 처리하기 위해선 프로미스를 이해해야 합니다.</p>
<p>또한 이제 여러분은 <code>fetch</code> 메서드가 더 쉽게 느껴지실 겁니다.</p>
<pre><code class="language-js">fetch('/api/user.json')
.then(function(response) {
    return response.json();
})
.then(function(json) {
    console.log(json); // {"name": "tapas", "blog": "freeCodeCamp"}
});
</code></pre>
<ul>
<li><code>fetch</code> 메서드는 프로미스를 반환합니다. 그래서 여기에 <code>.then</code> 핸들러를 호출할 수 있습니다.</li>
<li>나머지는 우리가 글에서 배웠던 프로미스 체인에 관한 내용입니다.</li>
</ul>
<h2 id="">마치기 전에</h2>
<p>지금까지 읽어주셔서 감사합니다. <a href="https://twitter.com/tapasadhikary">트위터(@tapasadhikary)</a>에서 소통해요.</p>
<p>좋아하실 만한 글들을 추천드릴게요.</p>
<ul>
<li><a href="https://blog.greenroots.info/javascript-undefined-and-null-lets-talk-about-it-one-last-time">JavaScript undefined and null: Let's talk about it one last time!</a></li>
<li><a href="https://blog.greenroots.info/javascript-equality-comparison-with-and-objectis">JavaScript: Equality comparison with ==, === and Object.is</a></li>
<li><a href="https://www.freecodecamp.org/news/javascript-this-keyword-binding-rules/">The JavaScript <code>this</code> Keyword +5 Key Binding Rules Explained for JS Beginners</a></li>
<li><a href="https://www.freecodecamp.org/news/javascript-typeof-how-to-check-the-type-of-a-variable-or-object-in-js/">JavaScript TypeOf - How to Check the Type of a Variable or Object in JS</a></li>
</ul>
<p>여기까지입니다. 다음 글에서 만나요. 그때까지 모두 잘 지내세요.</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 초보자를 위한 리액트 Context - 완벽 가이드 (2021) ]]>
                </title>
                <description>
                    <![CDATA[ 리액트 context는 모든 리액트 개발자들이 필수적으로 알아야 하는 개념입니다. context는 앱에서 state를 쉽게 공유할 수 있게 해줍니다. 이 글에서는 리액트 context가 무엇인지, 어떻게 사용하는지, 언제 사용하고 사용해서는 안 되는지 등에 대해서 알아보도록 하겠습니다. 리액트 context를 다뤄본 적이 없어도 괜찮습니다. 쉬운 단계별 예시를 통해 필요한 모든 내용을 알게 될 것입니다. 그럼 ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/cobojareul-wihan-riaegteu-context-wanbyeog-gaideu-2021/</link>
                <guid isPermaLink="false">63674fe4c0cb07062cace88b</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nayoung Gu ]]>
                </dc:creator>
                <pubDate>Wed, 09 Nov 2022 08:13:37 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2022/11/react-context-for-beginners.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/react-context-for-beginners/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">React Context for Beginners – The Complete Guide (2021)</a>
      </p><!--kg-card-begin: markdown--><h4 id="contextcontextstate">리액트 context는 모든 리액트 개발자들이 필수적으로 알아야 하는 개념입니다. context는 앱에서 state를 쉽게 공유할 수 있게 해줍니다.</h4>
<p>이 글에서는 리액트 context가 무엇인지, 어떻게 사용하는지, 언제 사용하고 사용해서는 안 되는지 등에 대해서 알아보도록 하겠습니다.</p>
<p>리액트 context를 다뤄본 적이 없어도 괜찮습니다. 쉬운 단계별 예시를 통해 필요한 모든 내용을 알게 될 것입니다.</p>
<p>그럼 시작해 봅시다!</p>
<blockquote>
<p>리액트를 처음부터 끝까지 배울 수 있는 가이드가 필요하시다면 <a href="https://reactbootcamp.com/">The React Bootcamp</a>를 확인하세요.</p>
</blockquote>
<h2 id="">목차</h2>
<ul>
<li>
<p><a href="#what-is-react-context">리액트 context란</a></p>
</li>
<li>
<p><a href="#when-should-you-use-react-context">리액트 context를 사용해야 할 때</a></p>
</li>
<li>
<p><a href="#what-problems-does-react-context-solve">리액트 context가 해결해주는 문제</a></p>
</li>
<li>
<p><a href="#how-do-I-use-react-context">리액트 context 사용 방법</a></p>
</li>
<li>
<p><a href="#what-is-the-useContext-hook">useContext 훅이란</a></p>
</li>
<li>
<p><a href="#you-may-not-need-context">context가 필요 없을 때</a></p>
</li>
<li>
<p><a href="#does-react-context-replace-redux">리액트 context가 리덕스를 대체하는가</a></p>
</li>
<li>
<p><a href="#react-context-caveats">리액트 context 사용 시 주의사항</a></p>
</li>
</ul>
<h2 id="h3idwhatisreactcontextcontexth3"></h2><h3 id="what-is-react-context">리액트 context란</h3>
<p>리액트 context는 앱에서 컴포넌트에게 props를 사용하지 않고 필요한 데이터를 넘겨주며 사용할 수 있게 해줍니다.</p>
<p><em>다시 말해, 리액트 context는 컴포넌트들이 데이터(state)를 더 쉽게 공유할 수 있도록 해줍니다.</em></p>
<h2 id="h3idwhenshouldyouusereactcontextcontexth3"></h2><h3 id="when-should-you-use-react-context">리액트 context를 사용해야 할 때</h3>
<p>리액트 context는 앱의 모든 컴포넌트에서 사용할 수 있는 데이터를 전달할 때 유용합니다.</p>
<p><strong>데이터 종류는 다음과 같습니다.</strong></p>
<ul>
<li>테마 데이터 (다크 모드 혹은 라이트 모드)</li>
<li>사용자 데이터 (현재 인증된 사용자)</li>
<li>로케일 데이터 (언어 혹은 지역)</li>
</ul>
<p>데이터는 자주 업데이트할 필요가 없는 리액트 context에 위치해야 합니다.</p>
<p>왜일까요? context는 전체적인 상태 관리를 위해 만들어진 시스템이 아니기 때문입니다. context는 데이터를 쉽게 사용하기 위해 만들어졌습니다.</p>
<p><em>리액트 context는 리액트 컴포넌트를 위한 전역 변수와 같다고 생각하면 됩니다.</em></p>
<h2 id="h3idwhatproblemsdoesreactcontextsolvecontexth3"></h2><h3 id="what-problems-does-react-context-solve">리액트 context가 해결해주는 문제</h3>
<p>리액트 context는 props drilling을 막는 데 도움을 줍니다.</p>
<p><strong>Props drilling</strong>이란 중첩된 여러 계층의 컴포넌트에게 props를 전달해 주는 것을 의미합니다. 해당 props를 사용하지 않는 컴포넌트들에게까지 말이죠.</p>
<p>props drilling의 예시를 보여드리겠습니다. 이 앱에서 모든 컴포넌트에 prop으로 전달된 theme 데이터에 접근할 수 있습니다.</p>
<p>하지만 보시다시피 <code>Header</code>와 같은 <code>App</code>의 직계 자식 컴포넌트 또한 props를 사용해 테마 데이터를 내려주어야 합니다.</p>
<pre><code class="language-js">export default function App({ theme }) {
  return (
    &lt;&gt;
      &lt;Header theme={theme} /&gt;
      &lt;Main theme={theme} /&gt;
      &lt;Sidebar theme={theme} /&gt;
      &lt;Footer theme={theme} /&gt;
    &lt;/&gt;
  );
}
function Header({ theme }) {
  return (
    &lt;&gt;
      &lt;User theme={theme} /&gt;
      &lt;Login theme={theme} /&gt;
      &lt;Menu theme={theme} /&gt;
    &lt;/&gt;
  );
}
</code></pre>
<p><em>이 예제의 문제는 무엇일까요?</em></p>
<p>문제는 <code>theme</code> prop을 사용하지 않는 많은 컴포넌트들에게까지 해당 prop을 내려주고 있다는 점입니다.</p>
<p><code>Header</code> 컴포넌트는 자식 컴포넌트에게 <code>theme</code>을 내려주기만 할 뿐 직접 필요로 하지 않습니다. 다시 말해, <code>User</code>, <code>Login</code>, <code>Menu</code> 컴포넌트가 <code>theme</code> 데이터를 직접 사용하는 게 더 나을 것입니다.</p>
<p>이렇듯 모든 곳에 props를 사용하는 것을 우회할 수 있어 props drilling 문제를 피할 수 있는 것이 리액트 context의 장점입니다.</p>
<h2 id="h3idhowdoiusereactcontextcontexth3"></h2><h3 id="how-do-I-use-react-context">리액트 context 사용 방법</h3>
<p>context는 리액트 버전 16부터 사용 가능한 리액트 내장 API입니다.</p>
<p>즉, 어떤 리액트 프로젝트라도 리액트를 import하면 context를 바로 생성하고 사용할 수 있습니다.</p>
<p><strong>리액트 context를 사용하기 위한 네 단계는 다음과 같습니다.</strong></p>
<ol>
<li><code>createContext</code> 메서드를 사용해 context를 생성한다.</li>
<li>생성된 context를 가지고 context provider로 컴포넌트 트리를 감싼다.</li>
<li><code>value</code> prop을 사용해 context provider에 원하는 값을 입력한다.</li>
<li>context consumer를 통해 필요한 컴포넌트에서 그 값을 불러온다.</li>
</ol>
<p><em>조금 헷갈리나요?</em> 생각보다 간단합니다.</p>
<p>다음 예제 코드를 보면 <code>App</code>에서 context를 사용해 이름을 전달해주고 <code>User</code>라는 하위 컴포넌트에서 불러옵니다.</p>
<pre><code class="language-js">import React from 'react';
export const UserContext = React.createContext();
export default function App() {
  return (
    &lt;UserContext.Provider value="Reed"&gt;
      &lt;User /&gt;
    &lt;/UserContext.Provider&gt;
  )
}
function User() {
  return (
    &lt;UserContext.Consumer&gt;
      {value =&gt; &lt;h1&gt;{value}&lt;/h1&gt;} 
      {/* prints: Reed */}
    &lt;/UserContext.Consumer&gt;
  )
}
</code></pre>
<p>위의 코드를 한 줄씩 살펴봅시다.</p>
<ol>
<li><code>App</code> 컴포넌트에서 <code>React.createContext()</code>를 사용해 context를 만들고 <code>UserContext</code> 변수에 결과를 담아두었습니다. 대부분의 경우, 컴포넌트는 다른 파일에 있기 때문에 여기서와 같이 export해 사용할 것입니다. <code>React.createContext()</code>를 사용하면 <code>value</code> prop에 초깃값을 정해줄 수 있다는 점에 유의하세요.</li>
<li><code>App</code> 컴포넌트에서 <code>UserContext</code>를 사용하고 있습니다. 정확히 말하면 <code>UserContext.Provider</code>입니다. 이렇게 만들어진 context는 컴포넌트인 <code>Provider</code>와 <code>Consumer</code>라는 두 가지 프로퍼티를 갖는 객체입니다. 앱의 모든 컴포넌트에 값을 내려주기 위해서는 Provider 컴포넌트로 감싸주어야 합니다(위 예제의 <code>User</code> 컴포넌트).</li>
<li><code>UserContext.Provider</code>에는 컴포넌트 트리 전체에 전달해주고 싶은 값을 넣습니다. 이를 위해 <code>value</code> prop을 사용했습니다(위 예제의 Reed).</li>
<li><code>User</code> 혹은 context에서 제공하는 값을 사용하고 싶은 컴포넌트에서는 <code>UserContext.Consumer</code>라는 consumer 컴포넌트를 사용해야 합니다. 전달된 값을 사용하기 위해서는 <strong>render props 패턴</strong>을 사용해야 합니다. 이는 단지 consumer 컴포넌트가 prop처럼 전달하는 함수입니다. 함수의 결괏값으로 <code>value</code>를 반환해 사용할 수 있습니다.</li>
</ol>
<h2 id="h3idwhatistheusecontexthookusecontexth3"></h2><h3 id="what-is-the-useContext-hook">useContext 훅이란</h3>
<p>위의 예시를 보면 context를 사용하기 위해 render props 패턴을 사용하는 것이 조금 이상해 보일 수 있습니다.</p>
<p>리액트 훅이 도입된 리액트 16.8부터는 context를 사용하는 다른 방법이 등장했습니다. 이제는 <strong>useContext 훅</strong>을 사용해 context를 사용할 수 있습니다.</p>
<p>render props를 사용하는 대신, context를 사용할 때 컴포넌트 최상단에서 <code>React.useContext()</code>에 전체 context 객체를 내려줄 수 있습니다.</p>
<p>useContext 훅을 사용한 예시는 다음과 같습니다.</p>
<pre><code class="language-js">import React from 'react';
export const UserContext = React.createContext();
export default function App() {
  return (
    &lt;UserContext.Provider value="Reed"&gt;
      &lt;User /&gt;
    &lt;/UserContext.Provider&gt;
  )
}
function User() {
  const value = React.useContext(UserContext);  
    
  return &lt;h1&gt;{value}&lt;/h1&gt;;
}
</code></pre>
<p><em>useContext 훅의 장점은 컴포넌트가 더욱 간결해지고 나만의 커스텀 훅을 만들 수 있다는 점입니다.</em></p>
<p>선호하는 패턴에 따라서 consumer 컴포넌트를 직접 사용할 수도 있고 useContext 훅을 사용할 수도 있습니다.</p>
<h2 id="h3idyoumaynotneedcontextcontexth3"></h2><h3 id="you-may-not-need-context">context가 필요 없을 때</h3>
<p>많은 개발자들이 하는 실수는 여러 레벨의 컴포넌트에 props를 내려줘야 하는 경우에 매번 context를 사용하는 것입니다.</p>
<p>여기 <code>App</code> 컴포넌트의 <code>username</code>과 <code>avatarSrc</code> 두 가지 props가 필요한 <code>Avatar</code>라는 중첩 컴포넌트가 있습니다.</p>
<pre><code class="language-js">export default function App({ user }) {
  const { username, avatarSrc } = user;
  return (
    &lt;main&gt;
      &lt;Navbar username={username} avatarSrc={avatarSrc} /&gt;
    &lt;/main&gt;
  );
}
function Navbar({ username, avatarSrc }) {
  return (
    &lt;nav&gt;
      &lt;Avatar username={username} avatarSrc={avatarSrc} /&gt;
    &lt;/nav&gt;
  );
}
function Avatar({ username, avatarSrc }) {
  return &lt;img src={avatarSrc} alt={username} /&gt;;
}
</code></pre>
<p>가능하다면 props가 필요 없는 컴포넌트들에게 여러 개의 props를 굳이 전달하고 싶지 않을 것입니다.</p>
<p><em>어떻게 하면 될까요?</em></p>
<p>prop drilling 하고 있다는 이유로 바로 context에 의지하는 것보다는 컴포넌트를 더 잘 구성함으로써 해결할 수 있습니다.</p>
<p>최상위 컴포넌트인 <code>App</code>만 <code>Avatar</code> 컴포넌트에 대해 알고 있으면 되기 때문에 <code>App</code> 내에 바로 avatar를 만드는 것입니다.</p>
<p>이렇게 하면 두 개의 props를 내려주는 것 대신 <code>avatar</code>라는 하나의 prop만 내려주면 됩니다.</p>
<pre><code class="language-js">export default function App({ user }) {
  const { username, avatarSrc } = user;
  const avatar = &lt;img src={avatarSrc} alt={username} /&gt;;
  return (
    &lt;main&gt;
      &lt;Navbar avatar={avatar} /&gt;
    &lt;/main&gt;
  );
}
function Navbar({ avatar }) {
  return &lt;nav&gt;{avatar}&lt;/nav&gt;;
}
</code></pre>
<p><em>요약하자면, 바로 context를 사용하지는 마세요. prop drilling을 피하기 위해 컴포넌트를 개선하여 설계할 수 있는지를 먼저 살펴보세요.</em></p>
<h2 id="h3iddoesreactcontextreplacereduxcontexth3"></h2><h3 id="does-react-context-replace-redux">리액트 context가 리덕스를 대체하는가</h3>
<p>맞기도 하고 틀리기도 합니다.</p>
<p>많은 리액트 초보자들에게 리덕스는 데이터를 더 쉽게 전달해 주는 도구로 여겨지곤 합니다. 리덕스는 리액트 context 자체와 함께 제공되기 때문입니다.</p>
<p>하지만 state를 <em>업데이트</em>하는 것이 아니라 컴포넌트에 전달만 해주는 경우라면 리덕스와 같은 전역 상태 관리 라이브러리는 필요하지 않을 수도 있습니다.</p>
<h2 id="h3idreactcontextcaveatscontexth3"></h2><h3 id="react-context-caveats">리액트 context 사용 시 주의사항</h3>
<p><em>리액트 context가 내려주는 값을 업데이트하는 것은 왜 불가능할까요?</em></p>
<p>리액트 context를 useReducer와 같은 훅과 결합해 서드파티 라이브러리 없이 임시 상태 관리 라이브러리를 생성하는 것이 가능하지만, 보통 성능상의 문제로 권장되지 않습니다.</p>
<p>이런 접근법은 리액트 context가 재렌더링을 초래한다는 문제가 있습니다.</p>
<p>만약 리액트 context provider에 객체를 내려주고 있고 그 객체의 프로퍼티가 업데이트된다면 무슨 일이 일어날까요? <em>그 context를 사용하고 있는 모든 컴포넌트에서 재렌더링이 일어날 것입니다.</em></p>
<p>state가 거의 없는 작은 앱의 경우(위 예제의 theme 데이터), 이러한 성능 이슈가 일어나지 않을 것입니다. 테마 데이터처럼 업데이트가 자주 되지 않는 경우라면 말이죠. 하지만 트리의 많은 컴포넌트에서 state 변경이 자주 일어난다면 문제가 될 것입니다.</p>
<h2 id="">결론</h2>
<p>이 가이드가 리액트 context 사용 방법을 이해하는 데 도움이 되었으면 좋겠습니다.</p>
<p>멋진 리액트 프로젝트에서 리액트 context를 사용하는 방법을 깊게 배워보고 싶다면 <a href="https://reactbootcamp.com/">리액트 부트 캠프</a>를 확인해 보세요.</p>
<h2 id="">리액트 전문가가 되고 싶으신가요? 리액트 부트 캠프에 참여하세요</h2>
<p><a href="https://reactbootcamp.com/">리액트 부트 캠프</a>에서는 리액트를 배울 때 필요한 모든 내용을 다루고 있으며 비디오, 치트 시트, 보너스 강의를 포함한 내용을 하나의 패키지로 제공합니다.</p>
<p>벌써 <strong>100명의 개발자</strong>들이 리액트 전문가가 되어 원하는 직업을 얻고 미래를 설계하며 공부한 내용을 확인해 보세요.</p>
<p><img src="https://reedbarger.nyc3.digitaloceanspaces.com/react-bootcamp-banner.png" alt="리액트 부트 캠프" width="600" height="400" loading="lazy"></p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ CSS 버튼 스타일 - Hover, Color, Background ]]>
                </title>
                <description>
                    <![CDATA[ 이 글에서는 CSS로 버튼을 꾸미는 방법에 대해 알아보겠습니다. 제 목표는 각기 다른 CSS 규칙과 스타일들이 어떻게 적용되어 사용되는지 보여드리는 것입니다. 따라서 디자인적인 요소나 스타일링 방법은 다루지 않을 예정입니다. 이보다는, 스타일 자체가 어떻게 적용되고 어떠한 속성들이 흔히 사용되는지, 그리고 스타일과 속성이 어떻게 같이 사용되는지에 대해 개괄적으로 다루겠습니다. 첫째로는 HTML로 버튼을 만드는 ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/css-beoteun-seutail-hover-color-background/</link>
                <guid isPermaLink="false">635a3e077c7a80063a4c8bf7</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nayoung Gu ]]>
                </dc:creator>
                <pubDate>Sat, 05 Nov 2022 14:04:17 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2022/11/moises-de-paula-HPZZHJ-LuDI-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/css-button-style-hover-color-and-background/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">CSS Button Style – Hover, Color, and Background</a>
      </p><!--kg-card-begin: markdown--><h4 id="css">이 글에서는 CSS로 버튼을 꾸미는 방법에 대해 알아보겠습니다.</h4>
<p>제 목표는 각기 다른 CSS 규칙과 스타일들이 어떻게 적용되어 사용되는지 보여드리는 것입니다. 따라서 디자인적인 요소나 스타일링 방법은 다루지 않을 예정입니다.</p>
<p>이보다는, 스타일 자체가 어떻게 적용되고 어떠한 속성들이 흔히 사용되는지, 그리고 스타일과 속성이 어떻게 같이 사용되는지에 대해 개괄적으로 다루겠습니다.</p>
<p>첫째로는 HTML로 버튼을 만드는 법에 대해 배우고 버튼의 기본 스타일을 덮을 수 있는 방법에 대해 알려드리겠습니다. 마지막으로는 버튼의 세 가지 다른 상태에 대해 어떻게 스타일링할 수 있는지에 대해서 간략하게 살펴보겠습니다.</p>
<h1 id="">목차</h1>
<ol>
<li><a href="#create-button-in-HTML">HTML에서 버튼 만들기</a></li>
<li><a href="#change-default-styling">버튼의 기본 스타일 바꾸기</a>
<ol>
<li><a href="#change-background-color">배경색 바꾸기</a></li>
<li><a href="#change-text-color">글자색 바꾸기</a></li>
<li><a href="#change-border-style">테두리 바꾸기</a></li>
<li><a href="#change-size">사이즈 바꾸기</a></li>
</ol>
</li>
<li><a href="#style-button-states">버튼 상태 꾸미기</a>
<ol>
<li><a href="#style-hover">hover 상태 꾸미기</a></li>
<li><a href="#style-focus">focus 상태 꾸미기</a></li>
<li><a href="#style-active">active 상태 꾸미기</a></li>
</ol>
</li>
<li><a href="#conclusion">결론</a></li>
</ol>
<p>그럼 시작해봅시다!</p>
<h3 id="create-button-in-HTML">HTML에서 버튼 만들기</h3>
<p>버튼을 만들기 위해서는 <code>&lt;button&gt;</code> 요소를 사용합니다.</p>
<p><code>&lt;div&gt;</code> 요소처럼 포괄적인 태그를 사용하는 것보다 <code>&lt;button&gt;</code> 태그를 사용하는 것이 접근성 측면에서 더 좋고 시맨틱한 방법입니다.</p>
<p>아래의 <code>index.html</code> 파일에서 웹 페이지의 기본 구조를 만들고 버튼 하나를 추가했습니다.</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;link rel="stylesheet" href="style.css"&gt;
    &lt;title&gt;CSS Button Style&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;button type="button" class="button"&gt;Click me!&lt;/button&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p><code>&lt;button type="button" class="button"&gt;Click me!&lt;/button&gt;</code> 코드를 분석해 봅시다.</p>
<ul>
<li>가장 먼저 <code>&lt;button&gt;</code> 태그로 열고 <code>&lt;/button&gt;</code> 태그로 닫음으로써 버튼을 추가했습니다.</li>
<li><code>&lt;button&gt;</code> 태그의 <code>type="button"</code> 속성은 명시적으로 클릭 가능한 버튼을 생성합니다. 이 버튼은 양식을 제출하기 위한 용도가 아니기 때문에 코드를 깔끔하게 작성하고 불필요한 결과를 초래하지 않기 위해 시맨틱하게 <code>type="button"</code>을 명시해 주는 것이 좋습니다.</li>
<li><code>class="button"</code> 속성은 별도의 CSS 파일에서 버튼을 스타일링하기 위해 사용될 예정입니다. 클래스명은 꼭 <code>button</code>일 필요는 없습니다. 예를 들어 <code>class="btn"</code>이라고 할 수도 있습니다.</li>
<li>버튼 내에 <code>Click me!</code>라는 텍스트가 보입니다.</li>
</ul>
<p>버튼에 적용되는 모든 스타일은 별도의 <code>style.css</code> 파일 내에 들어가게 됩니다.</p>
<p><code>index.html</code>에 <code>&lt;link rel="stylesheet" href="style.css"&gt;</code> 태그를 작성해서 두 개의 파일을 연결하면 HTML에 스타일을 적용할 수 있습니다.</p>
<p><code>style.css</code> 파일에 버튼을 화면 중앙에 배치하는 스타일을 추가했습니다.</p>
<p><code>class="button"</code>은 <code>.button</code> 선택자로 사용된 것을 확인할 수 있습니다. 버튼에 직접 스타일링할 수 있는 방법입니다.</p>
<pre><code class="language-css">* {
    box-sizing: border-box;
} 
body {
    display:flex;
    justify-content: center;
    align-items: center;
    margin:50px auto;
}
.button {
    position: absolute;
    top:50%
}
</code></pre>
<p>위의 코드는 다음과 같은 결과를 보여줍니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/Screenshot-2022-02-06-at-10.29.02-PM.png" alt="스타일링을 통해 바디의 중앙에 위치한 버튼의 모습" width="600" height="400" loading="lazy"></p>
<p>버튼의 기본 스타일은 여러분이 사용하고 있는 브라우저에 따라 상이하게 보입니다.</p>
<p>위의 예시는 구글의 크롬 브라우저에서 보이는 버튼의 기본 스타일입니다.</p>
<h3 id="change-default-styling">버튼 기본 스타일 바꾸기</h3>
<h3 id="change-background-color">버튼 배경색 바꾸기</h3>
<p>버튼의 배경 색은 CSS <code>background-color</code> 속성에 원하는 색상 값을 넣어 변경해 줄 수 있습니다.</p>
<p><code>.button</code> 선택자의 배경색을 바꾸기 위해 <code>background-color:#0a0a23;</code>를 지정했습니다.</p>
<pre><code class="language-css">.button {
    position: absolute;
    top:50%;
    background-color:#0a0a23;
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/Screenshot-2022-02-06-at-10.28.30-PM.png" alt="스타일링을 통해 어두운 배경색으로 바뀐 버튼의 모습" width="600" height="400" loading="lazy"></p>
<h3 id="change-text-color">버튼 글자색 바꾸기</h3>
<p>글자의 기본 배경색은 검은색이라 어두운 배경색을 넣으면 글자가 잘 보이지 않습니다.</p>
<p>따라서 가독성을 위해 버튼의 배경색과 글자색에 충분한 대비를 주는 것이 좋습니다.</p>
<p>이제 글자색을 바꾸기 위해 <code>color</code> 속성을 사용해 줍니다.</p>
<pre><code class="language-css">.button {
    position: absolute;
    top:50%;
    background-color:#0a0a23;
    color: #fff;
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/Screenshot-2022-02-06-at-10.28.03-PM.png" alt="스타일링을 통해 흰 글자색으로 바뀐 버튼의 모습" width="600" height="400" loading="lazy"></p>
<h3 id="change-border-style">버튼 테두리 바꾸기</h3>
<p>버튼의 회색 테두리가 보이시나요? 이 테두리는 버튼의 기본 스타일입니다.</p>
<p><code>border-color</code> 속성을 사용해 테두리를 수정할 수 있습니다. <code>background-color</code> 값과 동일하게 설정해 주면 버튼의 배경색과 테두리 색상이 같아지겠죠.</p>
<p>버튼의 테두리를 모두 지우려면 <code>border:none</code> 속성을 지정해 줍니다.</p>
<pre><code class="language-css">.button {
  position: absolute;
  top:50%;
  background-color:#0a0a23;
  color: #fff;
  border:none;
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/Screenshot-2022-02-06-at-10.27.33-PM.png" alt="스타일링을 통해 테두리가 지워진 버튼의 모습" width="600" height="400" loading="lazy"></p>
<p>그리고 <code>border-radius</code> 속성을 다음과 같이 사용해 버튼의 테두리를 둥글게 만들어줄 수 있습니다.</p>
<pre><code class="language-css">.button {
    position: absolute;
    top:50%;
    background-color:#0a0a23;
    color: #fff;
    border:none;
    border-radius:10px;
  }
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/Screenshot-2022-02-06-at-10.26.57-PM.png" alt="스타일링을 통해 테두리를 둥글게 만든 버튼의 모습" width="600" height="400" loading="lazy"></p>
<p>또한 다음과 같이 <code>box-shadow</code> 속성을 사용해 버튼 주변에 살짝 어두운 그림자 효과를 줄 수도 있습니다.</p>
<pre><code class="language-css"> position: absolute;
    top:50%;
    background-color:#0a0a23;
    color: #fff;
    border:none;
    border-radius:10px;
    box-shadow: 0px 0px 2px 2px rgb(0,0,0);
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/Screenshot-2022-02-06-at-10.25.55-PM.png" alt="스타일링을 통해 그림자 효과를 준 버튼의 모습" width="600" height="400" loading="lazy"></p>
<h3 id="change-size">버튼 사이즈 바꾸기</h3>
<p>버튼 테두리 내에 공간을 더 주고 싶으면 버튼에 <code>padding</code>을 추가합니다.</p>
<p>아래 코드에서 저는 버튼의 위, 아래, 오른쪽, 왼쪽에 padding을 15px씩 줬습니다.</p>
<p>그리고 <code>min-height</code>과 <code>min-width</code> 속성을 각각 사용해 최소 높이와 너비를 설정했습니다. 이렇게 하면 모든 종류의 디바이스에 따라 버튼 사이즈가 조정될 수 있습니다.</p>
<pre><code class="language-css">.button {
    position: absolute;
    top:50%;
    background-color:#0a0a23;
    color: #fff;
    border:none; 
    border-radius:10px; 
    padding:15px;
    min-height:30px; 
    min-width: 120px;
  }
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/Screenshot-2022-02-06-at-10.42.58-PM.png" alt="스타일링을 통해 크기가 변경된 버튼의 모습" width="600" height="400" loading="lazy"></p>
<h3 id="style-button-states">버튼의 상태 스타일 바꾸기</h3>
<p>버튼은 다음 세 가지 상태를 갖습니다.</p>
<ul>
<li><code>:hover</code></li>
<li><code>:focus</code></li>
<li><code>:active</code></li>
</ul>
<p>이 세 가지 상태는 각각 다른 스타일을 갖는 것이 좋습니다.</p>
<p>아래 섹션에서는 각각의 상태가 무엇을 의미하며 어떻게 발생하는지에 대해 간단히 설명하겠습니다. 그리고 각각의 상태에 따라 버튼에 스타일을 줄 수 있는 방법에 대해 살펴보겠습니다.</p>
<h3 id="style-hover">`hover` 상태 꾸미기</h3>
<p><code>:hover</code> 상태는 사용자가 버튼을 선택하거나 클릭하지 않고 마우스나 트랙패드를 올릴 때 발생합니다.</p>
<p>마우스를 올렸을 때의 버튼 스타일을 바꾸고 싶을 때는 <code>:hover</code>라는 CSS 가상 클래스(의사 클래스) 선택자를 사용할 수 있습니다.</p>
<p>보통 <code>:hover</code>를 사용해 버튼의 배경색을 바꾸곤 합니다.</p>
<p>부드럽게 배경색을 전환하려면 <code>:hover</code>와 <code>transition</code> 속성을 같이 사용합니다.</p>
<p><code>transition</code> 속성은 일반 상태에서 <code>:hover</code> 상태로 보다 부드럽게 바뀌는 <em>transition</em>을 만들어줍니다.</p>
<p><code>transition</code> 속성이 없을 때보다 배경색이 부드럽게 바뀌기 때문에 사용자 경험이 개선될 것입니다.</p>
<pre><code class="language-css">.button:hover {
      background-color:#002ead;
      transition: 0.7s;
  }
</code></pre>
<p>위 예시에서 버튼이 hover 되었을 때 배경색을 더 옅게 만들기 위해 Hex 색상 코드를 사용했습니다.</p>
<p>일반 상태에서 <code>:hover</code> 상태로 바뀔 때 <code>transition</code> 속성의 도움을 받아 <code>0.7s</code> 동안의 딜레이를 넣었습니다. 이렇게 함으로써 기존 배경 색인 <code>#0a0a23</code>에서 <code>#002ead</code>로 더 천천히 바뀌게 되는 것이죠.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/hover-2.gif" alt="hover 상태일 때 배경색이 바뀌는 버튼의 모습" width="600" height="400" loading="lazy"></p>
<p><code>:hover</code> 가상 클래스(의사 클래스)가 모바일 화면이나 앱에서 동작하지 않는다는 점에 유의하세요. hover 효과는 터치스크린이 아닌 데스크톱 웹 애플리케이션에서 사용하세요.</p>
<h3 id="style-focus">`focus` 상태 꾸미기</h3>
<p><code>:focus</code> 상태는 키보드를 사용하는 유저들에게 동작합니다 - 특히 <code>Tab</code>키(<code>⇥</code>)를 눌러 버튼이 포커스 되었을 때 활성화되는 상태입니다.</p>
<p>잘 따라오고 계신다면 <code>Tab</code> 키를 눌러 버튼을 포커스 했을 때 다음과 같이 보일 것입니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/focus-5.gif" alt="focus 상태일 때 파란색 테두리가 생기는 버튼의 모습" width="600" height="400" loading="lazy"></p>
<p>버튼이 포커스 되었을 때 쳐지는 얇은 파란색 테두리가 보이시나요?</p>
<p>브라우저는 키보드로 조작했을 때의 접근성 때문에 <code>:focus</code> 가상 클래스(의사 클래스)에 기본 스타일을 가지고 있습니다. <code>outline</code>을 같이 지워버리는 것은 권장하지 않습니다.</p>
<p>하지만 <code>outline</code>을 커스터마이징 해서 잘 보이게 할 수 있습니다.</p>
<p>먼저 <code>outline</code> 색상을 <code>transparent</code>로 설정합니다.</p>
<p>그리고 <code>outline-style</code>을 <code>solid</code>로 유지합니다. 마지막으로는 <code>box-shadow</code> 속성을 사용해서 요소가 포커스 되었을 때 원하는 색상을 추가할 수 있습니다.</p>
<pre><code class="language-css"> .button:focus {
    outline-color: transparent;
    outline-style:solid;
    box-shadow: 0 0 0 4px #5a01a7;
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/focusend.gif" alt="focus 상태일 때 보라색 그림자가 생기는 버튼의 모습" width="600" height="400" loading="lazy"></p>
<p>원하는 효과에 따라 이번에도 <code>transition</code> 속성을 같이 사용해 줄 수 있습니다.</p>
<pre><code class="language-css">  .button:focus {
    outline-color: transparent;
    outline-style:solid;
    box-shadow: 0 0 0 4px #5a01a7;
    transition: 0.7s;
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/focusend1.gif" alt="focus 상태일 때 천천히 보라색 그림자가 생기는 버튼의 모습" width="600" height="400" loading="lazy"></p>
<h3 id="style-active">`active` 상태 꾸미기</h3>
<p><code>:active</code> 상태는 마우스나 노트북 트랙 패드로 버튼을 눌렀을 때 <em>활성화</em> 됩니다.</p>
<p><code>:hover</code>와 <code>:focus</code> 상태를 적용한 버튼을 누르면 어떤 효과가 발생하는지 확인해 봅시다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/active-1.gif" alt="active 상태일 때 배경색과 그림자 스타일이 바뀌는 버튼의 모습" width="600" height="400" loading="lazy"></p>
<p><code>:hover</code> 상태의 스타일은 버튼을 클릭하기 전 마우스를 올렸을 때 적용됩니다.</p>
<p>버튼을 클릭하면 <code>:focus</code> 상태의 스타일도 적용됩니다. <code>:active</code>상태와 <code>:focus</code> 상태가 같이 적용되기 때문이죠.</p>
<p>하지만 <code>:focus</code>와 <code>:active</code>는 서로 다르다는 점에 유의하세요.</p>
<p><code>:focus</code> 상태는 요소가 포커스 되었을 때 적용되며 <code>:active</code> 상태는 유저가 요소를 <code>클릭</code>하기 위해 잡거나 누르고 있을 때 적용되는 상태입니다.</p>
<p>유저가 버튼을 클릭할 때의 스타일을 바꾸기 위해서는 <code>:active</code> CSS 가상 선택자(의사 선택자)에 스타일을 적용합니다.</p>
<p>이번에는 유저가 버튼을 클릭하고 있을 때의 배경색을 바꾸어보았습니다.</p>
<pre><code class="language-css">.button:active {
    background-color: #ffbf00;
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/activefinal.gif" alt="클릭할 때 검정색에서 노란색으로 배경색이 바뀌는 버튼의 모습" width="600" height="400" loading="lazy"></p>
<h3 id="conclusion">결론</h3>
<p>수고하셨습니다! 여러분은 오늘 CSS로 버튼을 스타일링할 수 있는 기본 방법에 대해 학습했습니다.</p>
<p>버튼의 상태에 따라 스타일링할 수 있는 방법뿐만 아니라 버튼의 배경색과 글자색을 바꿀 수 있는 방법에 대해서도 살펴보았습니다.</p>
<p>웹 디자인에 대해 더 공부하고 싶다면 freeCodeCamp의 <a href="https://www.freecodecamp.org/learn/2022/responsive-web-design/">반응형 웹 디자인 자격증</a>을 확인해 보세요. 이 쌍방향 수업에서는 15개의 토이 프로젝트와 5개의 최종 결과물을 만들며 HTML과 CSS를 학습할 수 있습니다.</p>
<p>위 자격증 과정은 아직 베타 버전이기 때문에 가장 안정적인 최신 버전은 <a href="https://www.freecodecamp.org/learn/responsive-web-design/">이곳에서</a> 확인해 볼 수 있습니다.</p>
<p>읽어주셔서 감사드립니다. 즐거운 코딩하세요!</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ cmd 삭제 명령어 - 윈도우에서 파일과 폴더를 삭제하는 방법 ]]>
                </title>
                <description>
                    <![CDATA[ 때로는 명령어로 무언가를 처리하는 것이 더 빠를 때가 있습니다. 이번 짧은 튜토리얼에서는 명령 프롬프트를 여는 방법과 기본적인 명령어와 플래그들, 그리고 명령 프롬프트에서 파일과 폴더를 삭제하는 방법에 대해 알아보겠습니다. 이미 DOS 명령어가 익숙하다면 건너뛰셔도 좋습니다. 명령 프롬프트 여는 방법 명령 프롬프트를 열려면 윈도우 키를 누르고 "cmd"라고 입력합니다. 그리고 "관리자 권한으로 실행(Run ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/cmd-sagje-myeongryeongeo-windoueseo-pailgwa-poldeoreul-sagjehaneun-bangbeob/</link>
                <guid isPermaLink="false">6357252e7c7a80063a4c8be7</guid>
                
                    <category>
                        <![CDATA[ Windows 10 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nayoung Gu ]]>
                </dc:creator>
                <pubDate>Thu, 27 Oct 2022 06:58:59 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2022/10/5fc9bc71e6787e098393991d.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/cmd-delete-folder-how-to-remove-files-and-folders-in-windows/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">cmd Delete Folder – How to Remove Files and Folders in Windows</a>
      </p><!--kg-card-begin: markdown--><h4 id="">때로는 명령어로 무언가를 처리하는 것이 더 빠를 때가 있습니다.</h4>
<p>이번 짧은 튜토리얼에서는 명령 프롬프트를 여는 방법과 기본적인 명령어와 플래그들, 그리고 명령 프롬프트에서 파일과 폴더를 삭제하는 방법에 대해 알아보겠습니다.</p>
<p>이미 DOS 명령어가 익숙하다면 <a href="#delete-with-del-command">건너뛰셔도</a> 좋습니다.</p>
<h2 id="">명령 프롬프트 여는 방법</h2>
<p>명령 프롬프트를 열려면 윈도우 키를 누르고 "cmd"라고 입력합니다.</p>
<p>그리고 "관리자 권한으로 실행(Run as Administrator)"을 클릭합니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/run-command-prompt-as-administrator.jpg" alt="명령 프롬프트" width="600" height="400" loading="lazy"></p>
<p>그러면 관리자 권한으로 실행된 명령 프롬프트가 보일 것입니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/command-prompt-new-window.jpg" alt="명령 프롬프트" width="600" height="400" loading="lazy"></p>
<p align="center">윈도우의 명령 프롬프트 스크린샷</p>
<p>관리자 권한으로 프롬프트를 열 수 없어도 괜찮습니다. "관리자 권한으로 실행" 대신 "열기"만 눌러도 정상적인 명령 프롬프트가 열릴 것입니다.</p>
<p>보호받는 파일을 삭제할 수 없을 수도 있다는 것이 유일한 차이점인데, 대부분의 경우 크게 문제 되지 않습니다.</p>
<h2 id="anamedeletewithdelcommanddela"><a name="delete-with-del-command"><code>del</code> 명령어로 파일 삭제하는 방법</a></h2>
<p>명령 프롬프트가 열리면 <code>cd</code>를 사용해 파일이 있는 폴더로 이동하세요.</p>
<p>데스크톱에 Test라는 디렉터리를 준비했습니다. 모든 중첩 파일과 폴더를 보기 위해 <code>tree /f</code> 명령어를 입력합니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/command-prompt-tree.jpg" alt="명령 프롬프트" width="600" height="400" loading="lazy"></p>
<p>파일을 삭제하기 위해 다음 명령어를 사용합니다: <code>del "&lt;파일명&gt;"</code></p>
<p>예를 들어, <code>Test file.txt</code>을 삭제하려면 <code>del "Test File.txt"</code>만 실행시키면 됩니다.</p>
<p>그러면 파일을 삭제할 것인지 물어보는 프롬프트가 뜰 것입니다. 맞다면 "y"를 치고 엔터를 누릅니다.</p>
<p><strong>주의:</strong> <code>del</code> 명령어로 삭제된 파일은 복구될 수 없습니다. 사용 시 주의하세요.</p>
<p>그리고 나면 파일이 삭제되었는지 확인하기 위해 <code>tree /f</code> 명령어를 실행합니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/del-tree-check.jpg" alt="명령 프롬프트" width="600" height="400" loading="lazy"></p>
<p>보너스 팁 - 명령 프롬프트는 자동 완성 기능이 있습니다. <code>del test</code>만 입력하고 탭 키를 누르면 프롬프트는 알아서 <code>del "Test File.txt"</code>로 바꿔줍니다.</p>
<h3 id="del"><code>del</code> 명령어로 파일 강제 삭제하는 방법</h3>
<p>가끔 읽기 전용인 파일인 경우, <code>del</code> 명령어를 사용하려고 하면 다음과 같은 에러가 날 것입니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/read-only-error.jpg" alt="명령 프롬프트" width="600" height="400" loading="lazy"><br>
이 문제를 해결하기 위해 <code>/f</code> 플래그를 사용할 수 있습니다. 예를 들어 <code>del /f "Read Only Test File.txt"</code>라고 말이죠.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/del-force-flag.jpg" alt="명령 프롬프트" width="600" height="400" loading="lazy"></p>
<h2 id="rmdir"><code>rmdir</code> 명령어로 폴더 삭제하는 방법</h2>
<p>디렉터리/폴더를 삭제하기 위해 <code>rmdir</code> 혹은 <code>rd</code> 명령어가 필요할 수 있습니다. 둘 다 같은 방식으로 동작하지만 더 명시적인 <code>rmdir</code>로 설명하겠습니다.</p>
<p>또한 앞으로 디렉터리와 폴더라는 용어를 번갈아 사용할 예정입니다. '폴더'는 데스크톱의 초창기 GUI에서 유명해진, 비교적 최신 용어입니다. 하지만 기본적으로 폴더와 디렉터리는 같은 의미입니다.</p>
<p>디렉터리를 삭제하려면 <code>rmdir &lt;디렉터리명&gt;</code> 명령어를 입력합니다.</p>
<p><strong>주의:</strong> <code>rmdir</code> 명령어로 삭제한 디렉터리는 복구될 수 없습니다. 사용 시 주의하세요.</p>
<p>지금은 Subfolder라는 디렉터리를 삭제하기 위해 <code>rmdir Subfolder</code>라는 명령어를 사용할 것입니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/directory-not-empty.jpg" alt="명령 프롬프트" width="600" height="400" loading="lazy"></p>
<p>혹시 기억하신다면, Subfolder는 Nested Test File이라는 파일을 가지고 있습니다.</p>
<p><code>cd</code>를 이용해 Subfolder 디렉터리로 들어가 파일을 삭제하고 <code>cd ..</code>로  다시 돌아와 <code>rmdir Subfolder</code> 명령어를 다시 실행할 수 있지만 이 방법은 너무 귀찮은 일입니다. 그리고 만약 중첩된 파일이나 디렉터리가 엄청 많다면 어떨지 상상해 보세요!</p>
<p><code>del</code> 명령어처럼 일을 훨씬 더 쉽고 빠르게 처리할 수 있도록 도와주는 유용한 플래그가 있습니다.</p>
<h3 id="rmdirs"><code>rmdir</code>와 <code>/s</code> 플래그 사용 방법</h3>
<p>중첩된 모든 파일과 하위 디렉터리를 포함한 디렉터리를 삭제하고 싶다면 <code>/s</code> 플래그만 사용하면 됩니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/rmdir-s-flag.jpg" alt="명령 프롬프트" width="600" height="400" loading="lazy"><br>
아마 프롬프트는 그 디렉터리를 삭제할 것인지 물어볼 것입니다. 맞다면, "y"를 누르고 엔터를 칩니다.</p>
<p>그럼 끝입니다! 윈도우 명령 프롬프트에서 파일과 폴더를 삭제할 수 있는 모든 방법에 대해 배웠습니다.</p>
<p>이 명령어들은 명령 프롬프트 버전 2.0인 파워셸(PowerShell)에서도 모두 동작할 것입니다. 또한 파워셸은 맥/리눅스 커맨드 라인에 익숙한 분들이라면 친숙할 <code>ls</code>나 <code>clear</code>과 같은 다양한 기능을 가지고 있습니다.</p>
<p>명령어들이 도움이 되었거나 혹시 다른 유용한 명령어들을 알고 계신다면 트위터로 꼭 알려주세요.</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 리액트 배경 이미지 튜토리얼 - CSS 인라인으로 backgroundImage를 설정하는 방법 ]]>
                </title>
                <description>
                    <![CDATA[ 리액트에서 인라인 CSS 방식으로 backgroundImage 속성을 지정하는 방법은 네 가지가 있습니다. 이번 튜토리얼에서는 각각의 예제 코드와 함께 이 네 가지 방법을 소개합니다. 리액트에서 외부 url을 사용해 배경 이미지를 설정하는 방법 만약 이미지가 온라인상 어딘가에 존재한다면 다음과 같이 요소에 url을 넣어 배경 이미지를 설정할 수 있습니다. function App() {   ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/riaegteu-baegyeong-imiji-tyutorieol-css-inraineuro-backgroundimagereul-seoljeonghaneun-bangbeob/</link>
                <guid isPermaLink="false">6340f52b86d53d05fdf62452</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nayoung Gu ]]>
                </dc:creator>
                <pubDate>Mon, 10 Oct 2022 07:06:29 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2022/10/fcc-bg-image-2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/react-background-image-tutorial-how-to-set-backgroundimage-with-inline-css-style/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">React Background Image Tutorial – How to Set backgroundImage with Inline CSS Style</a>
      </p><!--kg-card-begin: markdown--><h3 id="cssbackgroundimage">리액트에서 인라인 CSS 방식으로 <code>backgroundImage</code> 속성을 지정하는 방법은 네 가지가 있습니다.</h3>
<p>이번 튜토리얼에서는 각각의 예제 코드와 함께 이 네 가지 방법을 소개합니다.</p>
<h2 id="url">리액트에서 외부 url을 사용해 배경 이미지를 설정하는 방법</h2>
<p>만약 이미지가 온라인상 어딘가에 존재한다면 다음과 같이 요소에 url을 넣어 배경 이미지를 설정할 수 있습니다.</p>
<pre><code class="language-jsx">function App() {
  return (
    &lt;div style={{ 
      backgroundImage: `url("https://via.placeholder.com/500")` 
    }}&gt;
      Hello World
    &lt;/div&gt;
  );
}
</code></pre>
<p align="center">외부 url을 사용해 배경 이미지를 설정하는 방법</p>
<p>위의 코드는 <code>background-image: url(https://via.placeholder.com/500)</code>이라는 스타일이 적용된 <code>&lt;div&gt;</code> 요소를 렌더링할 것입니다.</p>
<h2 id="src">리액트에서 /src 폴더 내 배경 이미지를 불러오는 방법</h2>
<p>만약 여러분이 Create React App을 사용해 앱을 만들어 이미지가 <code>src/</code> 폴더 내에 있는 경우라면, 이미지를 먼저 <code>import</code> 한 후에 요소의 배경으로 설정할 수 있습니다.</p>
<pre><code class="language-js">import React from "react";
import background from "./img/placeholder.png";
function App() {
  return (
    &lt;div style={{ backgroundImage: `url(${background})` }}&gt;
      Hello World
    &lt;/div&gt;
  );
}
export default App;
</code></pre>
<p align="center">import한 이미지로 배경을 설정하는 방법</p>
<p><code>npm start</code> 명령어를 입력하면, 리액트는 "컴파일 실패"라는 에러를 보여주고 이미지를 찾지 못해 빌드를 멈출 것입니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/React-failed-to-compile-image.png" alt="컴파일 에러" width="600" height="400" loading="lazy"></p>
<p align="center">이미지를 찾지 못해 컴파일에 실패한 리액트</p>
<p>이렇게 되면 웹 앱에 깨진 이미지 링크가 표시되지 않습니다. 위의 코드에서 <code>backgroundImg</code> 값으로 템플릿 문자열이 사용되었기 때문에 자바스크립트 표현식을 포함할 수 있습니다.</p>
<h2 id="">리액트에서 상대 경로로 배경 이미지를 불러오는 방법</h2>
<p>Create React App에서 <code>public/</code> 폴더는 리액트 앱에 사용되는 정적 assets 파일들을 담고 있습니다. 이 폴더에 넣는 모든 파일은 온라인상으로 접근 가능합니다.</p>
<p><code>public/</code> 폴더 내에 <code>image.png</code> 파일을 넣게 되면 <code>&lt;호스트 주소&gt;/image.png</code> 경로로 접근할 수 있습니다. 로컬 컴퓨터에서 리액트를 실행한 경우라면 <code>http://localhost:3000/image.png</code>에서 이미지가 보일 것입니다.</p>
<p>배경 이미지를 설정하기 위해서는 호스트 주소에 상대 경로를 지정해주면 됩니다.</p>
<pre><code class="language-js">&lt;div style={{ backgroundImage: "url(/image.png)" }}&gt;
  Hello World
&lt;/div&gt;
</code></pre>
<p align="center">상대 경로로 배경 이미지를 설정하는 방법</p>
<p>위의 예시처럼 url 경로를 <code>/image.png</code>로 설정하면 브라우저는 <code>&lt;host 주소&gt;/image.png</code>에서 배경 이미지를 찾을 것입니다.</p>
<p>이미지들을 폴더 내에 따로 정리하고 싶다면 다음과 같이 <code>public/</code> 내에 다른 폴더를 만들 수도 있습니다.</p>
<p align="center"><img src="https://www.freecodecamp.org/news/content/images/2020/12/Screen-Shot-2020-12-14-at-20.18.30.png" width="600" height="400" alt="Screen-Shot-2020-12-14-at-20.18.30" loading="lazy"></p><p>
</p><p align="center">public/ 폴더 내에 img/ 폴더 만들기</p>
<p>만약 다른 폴더를 만들었다면 <code>backgroundImage</code>의 값으로 <code>url(/img/image.png)</code>를 넣어주는 것을 잊지 마세요.</p>
<h2 id="">리액트에서 절대 경로로 배경 이미지를 불러오는 방법</h2>
<p>Create React App의 <code>PUBLIC_URL</code> 환경 변수에 절대 경로를 포함해 다음과 같이 사용할 수도 있습니다.</p>
<pre><code class="language-js">&lt;div style={{ 
  backgroundImage: `url(${process.env.PUBLIC_URL + '/image.png'})` 
}}&gt;
  Hello World
&lt;/div&gt;
</code></pre>
<p align="center">절대 경로를 사용해 배경 이미지 설정하기</p>
<p>로컬 컴퓨터에서 이 코드를 실행하면 리액트 스크립트가 <code>PUBLIC_URL</code>의 값을 처리할 것입니다. 로컬에서는 절대 경로가 아닌 상대 경로처럼 보일 것입니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/absolute-url-background-image-1.png" alt="절대 경로 예시" width="600" height="400" loading="lazy"></p>
<p align="center">이미지의 절대 경로는 로컬 컴퓨터에서 보이지 않음</p>
<p>절대 경로는 나중에 리액트를 실제 앱으로 배포할 때 보이게 될 것입니다.</p>
<h2 id="">추가 속성과 함께 배경 이미지를 설정하는 방법</h2>
<p>배경 이미지를 추가적으로 커스터마이즈 하고 싶다면 다음 예시와 같이 <code>backgroundImage</code> 이후에 추가 속성을 지정해 줄 수 있습니다.</p>
<pre><code class="language-js">&lt;div style={{ 
  backgroundImage: `url(${process.env.PUBLIC_URL + '/image.png'})`,
  backgroundRepeat: 'no-repeat',
  width:'250px' 
}}&gt;
  Hello World
&lt;/div&gt;
</code></pre>
<p align="center">추가 속성과 함께 배경 이미지를 설정하는 방법</p>
<p>위에서 설정한 속성들은 <code>&lt;div&gt;</code> 요소에 배경 이미지를 넣고 <code>background-repeat: no-repeat</code>과 <code>width: 250px</code>을 지정하고 있습니다.</p>
<p>읽어주셔서 감사드리고, 이 글이 도움이 되었으면 좋겠습니다. 혹시 질문이 있으시면 제 <a href="https://twitter.com/nsebhastian">트위터</a>를 찾아주세요. 개발자들을 위한 짧은 팁들도 종종 올리겠습니다. 🙂</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 깃허브 프로젝트에 리드미 파일을 잘 작성하는 방법 ]]>
                </title>
                <description>
                    <![CDATA[ 처음 깃허브를 알게 되었을 때 저는 깃허브가 무엇인지 전혀 모르는 상태였습니다. 실은, 개발자라면 코드를 올리는 곳이 있어야 한다는 얘기를 듣고 깃허브 계정을 만들게 되었습니다. 오랜 기간 동안 입문자였던 저는 깃허브로 아무것도 하지 않았습니다. 하지만 기술에 대한 열정이 생기면서 다른 개발자들의 계정을 팔로우하고 깃허브에 올라온 그들의 작업물들을 보기 시작했습니다. 그리고 그들의 ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/gisheobeu-peurojegteue-rideumi-paileul-jal-jagseonghaneun-bangbeob/</link>
                <guid isPermaLink="false">630d6c75b9e784058602947a</guid>
                
                    <category>
                        <![CDATA[ Github ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nayoung Gu ]]>
                </dc:creator>
                <pubDate>Thu, 01 Sep 2022 06:21:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2022/09/uide-to-writting-a-good-readme-file--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/how-to-write-a-good-readme-file/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Write a Good README File for Your GitHub Project</a>
      </p><!--kg-card-begin: markdown--><h3 id="">처음 깃허브를 알게 되었을 때 저는 깃허브가 무엇인지 전혀 모르는 상태였습니다. 실은, 개발자라면 코드를 올리는 곳이 있어야 한다는 얘기를 듣고 깃허브 계정을 만들게 되었습니다.</h3>
<p>오랜 기간 동안 입문자였던 저는 깃허브로 아무것도 하지 않았습니다. 하지만 기술에 대한 열정이 생기면서 다른 개발자들의 계정을 팔로우하고 깃허브에 올라온 그들의 작업물들을 보기 시작했습니다. 그리고 그들의 공통점을 알게 되었습니다. 모두 멋진 프로젝트를 하고 오픈소스에 기여하며 구체적인 <strong>리드미 파일</strong>이 있다는 점이었죠.</p>
<p>그래서 리드미 파일에 대한 관심이 커지기 시작했고, 제 프로젝트에도 넣어볼까 생각하게 되었습니다. 사실 리드미 파일을 어떻게 써야 하는지도 모르는 채로 급하게 작성했습니다. 솔직히 말씀드리면 너무 형편없었죠. <a href="https://github.com/larymak/ToDo-list-App/tree/v1.0">여기</a>에서 확인해 보실 수 있습니다.</p>
<p>꽤 오랫동안 비슷한 상태였습니다. 하지만 계속 연습하고 공부하면서 <a href="https://github.com/larymak/Python-project-Scripts">이렇게</a> 나아진 리드미 문서는 프로젝트에 더 몰입하고 다른 개발자들의 참여를 유도하는 데 도움이 되었습니다.</p>
<p>좋은 리드미는 깃허브에 작업물을 올리는 수많은 개발자들 사이에서 자신을 돋보일 수 있게 해주는 중요한 수단입니다.</p>
<p>이 글에서는 좋은 리드미 파일이란 무엇이며, 어떻게 작성해야 하는지에 대해서 배울 것입니다. 먼저 리드미 파일의 의미를 이해해 봅시다.</p>
<h2 id="">리드미 파일이란?</h2>
<p>간단히 말해서, 리드미 파일은 사람들에게 여러분이 작업한 프로젝트에 대해 구체적인 설명을 제공하는 가이드라고 보시면 됩니다.</p>
<p>프로젝트를 어떻게 사용할 수 있는지에 대한 가이드 문서라고도 설명할 수 있겠네요. 일반적으로 리드미 파일은 프로젝트 설치 방법과 실행 방법을 명시합니다.</p>
<p>여러분이 개발자로서 리드미를 작성하며 프로젝트를 문서화할 줄 아는 것이 중요한 이유는 다음과 같습니다:</p>
<ul>
<li>
<p>리드미는 여러분의 프로젝트에서 가장 먼저 보이는 페이지이기 때문에 간단하면서도 구체적이어야 합니다.</p>
</li>
<li>
<p>리드미는 많은 프로젝트들 사이에서 여러분의 프로젝트를 돋보이게 합니다. 여러분의 프로젝트 또한 훌륭해야 한다는 점도 잊지 마세요.</p>
</li>
<li>
<p>리드미는 프로젝트가 무엇을 어떻게 전달하고 싶은지에 대해 여러분이 집중할 수 있게 도와줍니다.</p>
</li>
<li>
<p>리드미를 쓰면 글쓰기 능력이 향상됩니다. 프리드리히 니체(Friedrich Nietzsche)는 다음과 같이 말했습니다:</p>
</li>
</ul>
<blockquote>
<p>훌륭한 작가는 자신의 생각뿐만 아니라 친구들의 생각까지 마음에 품고 있다.</p>
</blockquote>
<p>프로젝트를 할 때, 다른 개발자들이 여러분의 코드를 이해해야 한다는 점을 염두에 두어야 합니다. 그래서 추가적인 가이드를 포함하는 것이 큰 도움이 될 것입니다.</p>
<p>예를 들어, 위에서 공유했던 제 <a href="https://github.com/larymak/ToDo-list-App/tree/v1.0">첫 번째 프로젝트</a>의 리드미는 형편없었죠. 프로젝트 자체는 훌륭했지만 초심자가 제 레포지토리를 클론하면 정확히 무엇이 일어나는지 이해하기 힘들었을 것입니다. 그 파일에 바이러스가 있는지 누가 알겠어요.</p>
<p>깃허브에 이런 프로젝트를 올리면, 얼마나 훌륭한지에 상관없이 다른 개발자들은 잘 쓰여진 리드미가 없으면 그 프로젝트에 대해 알아보거나 작업하고 싶지 않을 것입니다.</p>
<p>이제 이 <a href="https://github.com/larymak/Html-Css-Recap">프로젝트</a>를 살펴봅시다. 이번엔 이게 무슨 프로젝트인지, 어떤 내용을 포함하고 있는지, 이 프로젝트를 이용하거나 기여하기 위해선 어떻게 해야 하는지에 대해 알아볼 수 있습니다.</p>
<p>이게 바로 잘 쓰인 리드미의 위력이며, 이런 리드미는 여러분의 프로젝트를 변화시킬 수 있습니다.</p>
<p>그럼 이제 어떻게 작성해야 하는지에 대해 알아보겠습니다.</p>
<h2 id="">좋은 리드미를 작성하는 방법 - 단계별 가이드</h2>
<p>가장 주의해야 할 점은 좋은 리드미를 작성하는 방법은 한 가지가 아니라는 점입니다. 하지만 굉장히 잘못된 방법은 하나 있는데요, 그것은 리드미를 전혀 작성하지 않는 것입니다.</p>
<p>다양한 리드미 파일을 찾아보고 연구해 본 결과, 제가 발견한 가장 확실한 방법이 있습니다. 이것을 공유드리고자 합니다. 제가 종종 스스로 되뇌이는 것처럼 말이죠:</p>
<blockquote>
<p>매일이 학습의 날이다.</p>
</blockquote>
<p>여러분이 커리어를 쌓아나가면서, 리드미를 잘 쓰고 발전시킬 수 있는 여러분만의 방식을 찾아 나가게 될 것입니다. 아마 여러분은 자신만의 독특한 가이드를 만들게 될 수도 있습니다.</p>
<p>본격적으로 시작하기 전에 주의해야 할 점이 있습니다. 프로젝트의 리드미를 작성할 때, 프로젝트의 <strong>무엇을, 왜, 어떻게</strong>에 대해서 답해야 한다는 점입니다.</p>
<p>이것을 도와줄 가이드 질문들이 있습니다:</p>
<ul>
<li>동기가 무엇이었나요?</li>
<li>왜 이 프로젝트를 기획했나요?</li>
<li>이 프로젝트는 어떤 문제를 해결하나요?</li>
<li>이 프로젝트를 통해 무엇을 배우셨나요?</li>
<li>이 프로젝트의 특징은 무엇인가요?<br>
만약 프로젝트의 특징이 너무 많다면, '특징' 섹션을 추가해서 이곳에 나열하세요.</li>
</ul>
<h2 id="">리드미에 들어가야 하는 내용</h2>
<h3 id="1">1. 프로젝트명</h3>
<p>먼저 프로젝트의 이름입니다. 한 문장으로 전체 프로젝트를 설명하고 사람들이 프로젝트의 주 목표가 무엇인지 이해할 수 있게 도와줍니다.</p>
<h3 id="2">2. 프로젝트 설명</h3>
<p>이 부분은 많은 개발자들이 간과하는 프로젝트의 주요 요소입니다.</p>
<p>설명은 프로젝트에서 굉장히 중요한 부분을 차지합니다. 잘 다듬어진 설명을 통해 미래의 인사 담당자뿐만 아니라 다른 개발자들에게도 여러분의 작업물을 자랑할 수 있습니다.</p>
<p>리드미에서 설명의 질은 좋은 프로젝트와 그렇지 못한 프로젝트를 구분 짓습니다. 잘 쓰인 리드미를 통해 다음과 같은 내용을 설명하고 소개할 수 있습니다.</p>
<ul>
<li>여러분의 애플리케이션이 무엇을 하는지,</li>
<li>왜 그 기술을 사용했는지,</li>
<li>여러분이 당면했던 문제나 나중에 추가하고 싶은 기능이 무엇인지</li>
</ul>
<h3 id="3">3. 목차 (선택)</h3>
<p>만약 리드미가 너무 길다면, 사용자들이 다른 섹션으로 쉽게 이동할 수 있게 목차를 만들 수도 있습니다. 목차를 통해 독자들이 쉽게 프로젝트를 살펴볼 수 있을 것입니다.</p>
<h3 id="4">4. 프로젝트 설치 및 실행 방법</h3>
<p>만약 사용자가 따로 설치하거나 포스기와 같은 기계에서 실행해야 하는 프로젝트를 작업하고 있다면, 프로젝트를 설치할 수 있는 방법과 필요한 경우 dependencies를 포함해 작성해야 합니다.</p>
<p>개발 환경을 세팅하고 실행할 수 있는 단계적인 설명을 제공하세요.</p>
<h3 id="5">5. 프로젝트 사용 방법</h3>
<p>사용자/기여자들이 프로젝트를 이용할 수 있는 방법과 예시를 작성하세요. 예상 문제에 대해 항상 참고할 수 있는 곳을 마련함으로써 그들이 문제에 직면했을 때 쉽게 해결할 수 있을 것입니다.</p>
<p>프로젝트 실행 예시 화면의 스크린샷과 같은 시각 자료를 사용할 수도 있고 프로젝트에서 사용된 구조나 디자인 원칙을 추가할 수 있습니다.</p>
<p>프로젝트에 비밀번호나 유저 네임이 필요한 경우 계정을 적어두는 것도 좋은 방법입니다.</p>
<h3 id="6">6. 팀원 및 참고 자료</h3>
<p>만약 팀이나 조직 단위로 작업한 프로젝트라면 팀원들을 같이 기재하세요. 팀원들의 깃허브 프로필과 SNS 링크도 연결해야 합니다.</p>
<p>사용자가 프로젝트를 설치하는 데 도움을 줄 수 있는 튜토리얼이나 자료를 참고했다면 그런 링크도 같이 첨부해야 합니다.</p>
<p>이렇게 함으로써 감사를 표현함과 동시에 사람들이 프로젝트의 첫 번째 사본을 얻을 수 있습니다.</p>
<h3 id="7">7. 라이센스</h3>
<p>대부분의 리드미에서 라이센스는 가장 마지막으로 고려되는 부분입니다. 라이센스를 보고 다른 개발자들은 여러분의 프로젝트로 무엇을 할 수 있고 무엇을 할 수 없는지 알 수 있습니다.</p>
<p>우리는 작업하고 있는 프로젝트의 종류에 따라 다른 라이센스를 가지고 있습니다. 여러분이 고르는 라이센스에 따라서 프로젝트의 기여가 달라질 수 있습니다.</p>
<p>가장 흔한 라이센스는 다른 사람들이 여러분의 코드를 수정할 수 있고 상업적인 용도로 사용할 수 있는 GPL 라이센스입니다. 라이센스를 고를 때 도움을 받고 싶다면, 이 링크를 확인해 보세요: <a href="https://choosealicense.com/">https://choosealicense.com/</a></p>
<p>지금까지 좋은 리드미를 위한 최소한의 요건을 살펴보았습니다. 하지만 눈길을 더 사로잡고 상호적인 리드미를 만들고 싶다면 다음 내용을 추가해 볼 수 있습니다.</p>
<h2 id="">추가적인 부분</h2>
<h3 id="8">8. 뱃지</h3>
<p>뱃지는 꼭 필요하진 않지만, 뱃지를 사용하면 다른 개발자들이 프로젝트에 대해 쉽게 알 수 있습니다.</p>
<p>이 섹션이 있으면 중요한 툴로 연결해 주거나 포크, 기여자, 오픈된 이슈 등 여러분의 프로젝트와 관련된 간단한 통계를 보여줄 수도 있습니다.</p>
<p>다음 사진은 뱃지 사용 방법을 보여주는 제 프로젝트의 스크린샷입니다:<br>
<img src="https://www.freecodecamp.org/news/content/images/2021/11/check.png" alt="뱃지 캡쳐" width="600" height="400" loading="lazy"></p>
<p>이 부분의 좋은 점은 자동으로 업데이트된다는 점입니다.</p>
<p>어디서 뱃지를 받는지 모르시겠다고요? <a href="https://shields.io/">shields.io</a>에서 제공하는 뱃지를 확인해 보세요. 바로 사용할 수 있는 수많은 뱃지가 있습니다. 지금은 다 이해할 수 없어도 시간이 지나면 다 알게 될 것입니다.</p>
<h3 id="9">9. 프로젝트에 기여하는 방법</h3>
<p>이 부분은 다른 개발자들이 기여할 수 있는 오픈 소스 프로젝트를 작업하고 있는 경우라면 가장 유용한 섹션일 것입니다. 여러분은 아마 다른 개발자들이 프로젝트에 어떻게 기여할 수 있는지에 대한 가이드라인을 추가하고 싶을 것입니다.</p>
<p>또한 향후 충돌을 막기 위해서 오픈소스 프로젝트에 사용되는 라이센스가 올바른 것인지 확인하는 것이 중요합니다. 기여 가이드라인을 추가하면 큰 도움이 되겠죠.</p>
<p>가장 잘 알려진 가이드라인으로는 <a href="https://www.contributor-covenant.org/">Contributor Covenant</a>와 <a href="https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/setting-guidelines-for-repository-contributors">기여 가이드</a>가 있습니다. 프로젝트의 규칙을 정할 때 이 문서들이 도움이 될 것입니다.</p>
<h3 id="10">10. 테스트</h3>
<p>더 나아가 애플리케이션의 테스트를 위해 예제 코드와 실행 방식을 작성하세요.</p>
<p>이렇게 함으로써 여러분의 프로젝트가 문제없이 작동할 것이라는 확신과 자신감을 보여줄 수 있고, 다른 사람들 또한 확신을 갖게 될 것입니다.</p>
<h3 id="">참고사항</h3>
<p>다음은 여러분이 리드미를 작성할 때 참고하면 좋을만한 내용들입니다:</p>
<ul>
<li>내용을 최신으로 유지하기- 항상 파일을 최신 상태로 유지하는 것이 좋습니다. 변경 사항이 있는 경우 필요한 부분의 리드미를 꼭 업데이트해두세요.</li>
<li>언어 선택하기- 우린 모두 다른 지역에서 왔고 서로 다른 언어를 사용합니다. 그렇다고 코드를 번역해야 한다는 뜻은 아닙니다. 영어는 글로벌하게 사용되므로 영어로만 리드미를 작성하면 됩니다. 만약 타깃층이 영어에 친숙하지 않다면 번역기를 사용할 수 있겠죠.</li>
</ul>
<h2 id="">마무리</h2>
<p>리드미를 고쳐나가거나 첫 번째 리드미를 작성하기 위해 필요한 모든 내용을 살펴보았습니다.</p>
<p>이제 저는 여러분이 기존 프로젝트나 다음 프로젝트에서 프로젝트를 더 돋보이게 하기 위한 상호적이면서도 유익한 가이드를 추가할 수 있을 것이라고 확신합니다.</p>
<p>프로젝트에 자동으로 리드미를 생성해 주는 도구가 많긴 하지만, 자동화하기 전에 직접 먼저 만들어 보는 것이 좋습니다. 자동화 도구들을 확인하고 싶으시다면 다음 링크를 참고해 보세요:</p>
<ul>
<li><a href="https://www.makeareadme.com/">Make a README</a></li>
<li><a href="https://rahuldkjain.github.io/gh-profile-readme-generator/">README Generator</a></li>
<li><a href="https://github.com/kefranabg/readme-md-generator">README</a></li>
</ul>
<p>여기까지 읽어주셨다면 진심으로 감사드립니다. 이 글이 재미있었거나 도움이 되었다면 다른 개발자들도 프로젝트를 보완할 수 있게 공유해주세요.</p>
<p>여러분이 새로 만든 리드미 파일도 보고 싶네요. 다음 링크를 통해 꼭 공유해 주세요.</p>
<p><a href="https://twitter.com/larymak1">Twitter</a> | <a href="https://www.youtube.com/channel/UCrT1ARRZfLOuf6nc_97eXEg">YouTube</a> | <a href="https://www.linkedin.com/in/hillary-nyakundi-3a64b11ab/">LinkedIn</a> | <a href="https://github.com/larymak">GitHub</a>로 소통해요.</p>
<p>여러분의 소중한 의견도 공유해 주세요. 솔직한 피드백도 환영합니다!</p>
<p>즐거운 코딩하세요❤</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ CSS로 이미지를 중앙 정렬하는 방법 ]]>
                </title>
                <description>
                    <![CDATA[ 많은 개발자들이 이미지 작업을 할 때 어려움을 느끼곤 합니다. 반응형 [https://www.freecodecamp.org/news/css-responsive-image-tutorial/]과 정렬, 특히 이미지를 페이지 정중앙에 배치하는 것을 어려워합니다. 이번 글에서는 다양한 CSS 속성을 활용해 이미지를 수직 및 수평으로 가운데 정렬할 수 있는 가장 일반적인 방법을 소개해 드리려고 합니다. CSS Position [https://www.freecodecamp.org/news/how-to-use-the-position-property-in-css-to-align-elements-d8f49c403a26/] 과 Display [https://www.youtube.com/watch?v=hgoFi0fCv3w] 속성은 이전 글에서 다룬 적이 ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/how-to-center-an-image-in-css/</link>
                <guid isPermaLink="false">62dfb47ab9e78405860290e7</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nayoung Gu ]]>
                </dc:creator>
                <pubDate>Thu, 28 Jul 2022 05:21:28 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2022/07/5f9c9a4c740569d1a4ca24c2.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/how-to-center-an-image-in-css/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Center an Image Vertically and Horizontally with CSS</a>
      </p><!--kg-card-begin: markdown--><h3 id="">많은 개발자들이 이미지 작업을 할 때 어려움을 느끼곤 합니다. <a href="https://www.freecodecamp.org/news/css-responsive-image-tutorial/">반응형</a>과 정렬, 특히 이미지를 페이지 정중앙에 배치하는 것을 어려워합니다.</h3>
<p>이번 글에서는 다양한 CSS 속성을 활용해 이미지를 수직 및 수평으로 가운데 정렬할 수 있는 가장 일반적인 방법을 소개해 드리려고 합니다.</p>
<p>CSS <a href="https://www.freecodecamp.org/news/how-to-use-the-position-property-in-css-to-align-elements-d8f49c403a26/">Position</a>과 <a href="https://www.youtube.com/watch?v=hgoFi0fCv3w">Display</a> 속성은 이전 글에서 다룬 적이 있습니다. 아직 이 속성들이 낯설게 느껴지신다면, 이번 글을 읽기 전에 이전 포스트를 읽어보시길 추천해 드립니다.</p>
<p>관심 있는 분들을 위해 영상 버전도 첨부합니다:<br><br>
<a href="https://www.youtube.com/watch?v=mwVNVxpkly0?t=0s"><img src="https://img.youtube.com/vi/mwVNVxpkly0/0.jpg" alt="Video Label" width="480" height="360" loading="lazy"></a></p>
<h2 id="">수평적으로 이미지 가운데 정렬하기</h2>
<p>먼저 CSS 속성을 사용해 이미지를 수평 중앙에 정렬할 수 있는 세 가지 방법에 대해 알아봅시다.</p>
<h3 id="textalign">Text-Align</h3>
<p>이미지를 가로 중앙에 배치할 수 있는 첫 번째 방법은 <code>text-align</code> 속성을 사용하는 것입니다. 하지만 이 방법은 이미지가 <code>&lt;div&gt;</code>와 같은 블록 레벨 컨테이너 안에 있을 때만 작동합니다:</p>
<pre><code>&lt;style&gt;
    div {
        text-align: center;
    }
&lt;/style&gt;
&lt;div&gt;
    &lt;img src="your-image.jpg"&gt;
&lt;/div&gt;
</code></pre>
<h3 id="marginauto">Margin: Auto</h3>
<p>또 다른 방법은 <code>margin: auto</code> 속성을 사용하는 것입니다.</p>
<p>하지만 <code>margin: auto</code> 자체만으로는 이미지를 중앙 정렬할 수 없습니다. <code>margin: auto</code>를 사용하려면 다른 두 가지 속성을 함께 적용해야 합니다.</p>
<p>margin-auto 속성은 인라인 요소에 아무 영향을 미치지 않습니다. 따라서 인라인 요소인 <code>&lt;img&gt;</code> 태그의 경우 먼저 이를 블록 레벨 요소로 먼저 바꿔주어야 합니다.</p>
<pre><code>img {
    margin: auto;
    display: block;
}
</code></pre>
<p>두 번째는 너비입니다. 왼쪽과 오른쪽 마진이 나머지 빈 공간에 자리를 잡아 자동으로 가운데 정렬이 적용되기 때문입니다. 단, 너비가 100%로 지정되지 않은 경우에 한해서요.</p>
<pre><code>img {
    width: 60%;
    margin: auto;
    display: block;
}
</code></pre>
<h3 id="displayflex">Display: Flex</h3>
<p>세 번째 방법은 <code>display: flex</code>를 사용하는 것입니다. 컨테이너 요소에 <code>text-align</code> 속성을 사용했던 것처럼 컨테이너에 <code>display: flex</code>를 똑같이 사용해 줄 수 있습니다.</p>
<p>하지만 <code>display: flex</code>만으로는 부족합니다. 컨테이너 요소는 <code>justify-content</code>라 불리는 추가적인 속성을 가져야 합니다:</p>
<pre><code>div {
    display: flex;
    justify-content: center;
}
img {
    width: 60%;
}
</code></pre>
<p><code>justify-content</code>는 이미지를 수평 중앙 정렬을 할 때 <code>display: flex</code>와 함께 사용되는 속성입니다.</p>
<p>마지막으로 이미지의 너비는 컨테이너의 넓이보다 작아야 합니다. 그렇지 않으면 이미지가 공간 너비의 100%를 차지하게 되므로 중앙 정렬을 할 수 없습니다.</p>
<p><strong>주의할 점:</strong> <code>display: flex</code> 속성은 오래된 버전의 브라우저에서는 지원되지 않습니다. 구체적인 내용은 <a href="https://caniuse.com/?search=display%20flex">이 링크</a>를 참고하세요.</p>
<h2 id="">수직적으로 이미지 가운데 정렬하기</h2>
<h3 id="displayflex">Display: Flex</h3>
<p>수직 정렬을 할 때도 <code>display: flex</code>가 큰 도움이 될 수 있습니다.</p>
<p>컨테이너 요소의 높이가 800px이고, 이미지의 높이가 500px밖에 되지 않는다고 가정해 봅시다.</p>
<pre><code>div {
    display: flex;
    justify-content: center;
    height: 800px;
}
img {
    width: 60%;
    height: 500px;
}
</code></pre>
<p>이때는 <code>align-items: center</code>라는 한 줄의 코드만으로 중앙 정렬을 할 수 있습니다.</p>
<pre><code>div {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 800px;
}
</code></pre>
<p><code>align-items</code>는 <code>display: flex</code>와 함께 사용될 때 요소를 수직으로 정렬하는 속성입니다.</p>
<h3 id="positionabsolutetransform">Position: Absolute &amp; Transform 속성</h3>
<p>또 다른 방법은 <code>position</code>과 <code>transform</code> 속성을 함께 사용하는 것입니다. 이 방법은 조금 복잡하기 때문에 한 단계씩 해보겠습니다.</p>
<h3 id="step1positionabsolute">Step 1: Position Absolute 설정하기</h3>
<p>먼저, 이미지의 <code>position</code> 속성을 <code>static</code>에서 <code>absolute</code>로 바꿔줍니다.</p>
<pre><code>div {
    height: 800px;
    position: relative;
    background: red;
}
img {
    width: 80%;
    position: absolute;
}
</code></pre>
<p>이미지가 상대적으로 위치한 컨테이너 요소 안에 위치해야 하므로, 컨테이너 <code>div</code>에 <code>position: relative</code> 속성을 부여해줍니다.</p>
<h3 id="step2topleft">Step 2: Top &amp; Left 속성 부여하기</h3>
<p>두 번째로, 이미지의 위와 왼쪽 속성을 50%로 설정합니다. 이렇게 하면 이미지의 시작 지점(좌상단)이 컨테이너의 중간 지점이 될 것입니다.</p>
<pre><code>img {
    width: 80%;
    position: absolute;
    top: 50%;
    left: 50%;
}
</code></pre>
<h3 id="step3transform">Step 3: Transform 속성 부여하기</h3>
<p>위의 두 번째 단계까지 적용하면 이미지의 일부가 컨테이너의 밖으로 빠져나오게 됩니다. 이제 이미지를 다시 돌려놓아야 합니다.</p>
<p><code>transform</code> 속성으로 X축, Y축 모두 -50%를 적용하면 비로소 이미지가 중앙에 배치됩니다.</p>
<pre><code>img {
    width: 80%;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}
</code></pre>
<p>이 외에도 다른 방법들이 더 있지만 가장 일반적으로 사용되는 방법들을 소개해 드렸습니다. 이 글이 이미지 가운데 정렬을 이해하는 데 도움이 되었으면 좋겠습니다. 영어 원문을 읽고 싶으시다면 <a href="https://www.freecodecamp.org/news/how-to-center-an-image-in-css/">How to Center an Image Vertically and Horizontally with CSS</a>를 읽어보세요.</p>
<p><strong>웹 개발을 더 공부하고 싶으시다면 제 <a href="https://www.youtube.com/channel/UC1EgYPCvKCXFn8HlpoJwY3Q?view_as=subscriber">유튜브 채널</a>을 방문해 주세요.</strong></p>
<p>읽어주셔서 감사합니다!</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 초보자를 위한 40가지의 자바스크립트 프로젝트 ]]>
                </title>
                <description>
                    <![CDATA[ 프로그래밍 언어를 공부하기 가장 좋은 방법은 프로젝트를 해보는 것입니다. 누구나 쉽게 해볼 수 있는 40가지의 바닐라 자바스크립트, 리액트, 타입스크립트 프로젝트 튜토리얼을 만들었습니다. 먼저 영상을 보고 프로젝트를 만든 뒤 이를 자신만의 방법으로 다시 만들어보는 걸 추천합니다. 새로운 기능을 추가해 보거나 다른 방법을 사용해서 구현해 보세요. 이렇게 함으로써 개념을 잘 이해했는지 확인할 ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/javascript-projects-for-beginners/</link>
                <guid isPermaLink="false">62dfb3afb9e78405860290dd</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nayoung Gu ]]>
                </dc:creator>
                <pubDate>Wed, 27 Jul 2022 08:29:17 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2022/07/60599216687d62084bf6ac9e.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/javascript-projects-for-beginners/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">40 JavaScript Projects for Beginners – Easy Ideas to Get Started Coding JS</a>
      </p><!--kg-card-begin: markdown--><h3 id="">프로그래밍 언어를 공부하기 가장 좋은 방법은 프로젝트를 해보는 것입니다.</h3>
<p>누구나 쉽게 해볼 수 있는 40가지의 바닐라 자바스크립트, 리액트, 타입스크립트 프로젝트 튜토리얼을 만들었습니다.</p>
<p>먼저 영상을 보고 프로젝트를 만든 뒤 이를 자신만의 방법으로 다시 만들어보는 걸 추천합니다. 새로운 기능을 추가해 보거나 다른 방법을 사용해서 구현해 보세요.</p>
<p>이렇게 함으로써 개념을 잘 이해했는지 확인할 수 있을 것입니다.</p>
<p>아래의 리스트에서 프로젝트 이름을 클릭하면 해당 섹션으로 이동합니다.</p>
<h2 id="">바닐라 자바스크립트 프로젝트</h2>
<ol>
<li><a href="#flipper">Color Flipper 만들기</a></li>
<li><a href="#counter">카운터 만들기</a></li>
<li><a href="#how-to-create-a-review-carousel">리뷰 캐러셀 만들기</a></li>
<li><a href="#how-to-create-a-responsive-navbar">반응형 내비게이션 바 만들기</a></li>
<li><a href="#how-to-create-a-sidebar">사이드바 만들기</a></li>
<li><a href="#how-to-create-a-modal">모달 만들기</a></li>
<li><a href="#how-to-create-a-faq-page">FAQ 페이지 만들기</a></li>
<li><a href="#how-to-create-a-restaurant-menu-page">식당 메뉴판 만들기</a></li>
<li><a href="#how-to-create-a-video-background">비디오 배경 만들가</a></li>
<li><a href="#how-to-create-a-navigation-bar-on-scroll">스크롤바를 따라 움직이는 내비게이션 바 만들기</a></li>
<li><a href="#how-to-create-tabs-that-display-different-content">서로 다른 내용을 보여주는 탭 만들기</a></li>
<li><a href="#how-to-create-a-countdown-clock">카운트다운 시계 만들기</a></li>
<li><a href="#how-to-create-your-own-lorem-ipsum">나만의 Lorem ipsum 만들기</a></li>
<li><a href="#how-to-create-a-grocery-list">식료품 리스트 만들기</a></li>
<li><a href="#how-to-create-an-image-slider">이미지 슬라이더 만들기</a></li>
<li><a href="#how-to-create-a-rock-paper-scissors-game">가위바위보 게임 만들기</a></li>
<li><a href="#how-to-create-a-simon-game">사이먼 게임 만들기</a></li>
<li><a href="#how-to-create-a-platformer-game">플랫포머 게임 만들기</a></li>
<li><a href="#how-to-create-doodle-jump-and-flappy-bird">두들 점프 만들기</a></li>
<li><a href="#how-to-create-doodle-jump-and-flappy-bird">플래피 버드 만들기</a></li>
<li><a href="#how-to-create-seven-classic-games-with-ania-kubow">메모리 게임 만들기</a></li>
<li><a href="#how-to-create-seven-classic-games-with-ania-kubow">두더지 잡기 게임 만들기</a></li>
<li><a href="#how-to-create-seven-classic-games-with-ania-kubow">커넥트 포 게임 만들기</a></li>
<li><a href="#how-to-create-seven-classic-games-with-ania-kubow">뱀 게임 만들기</a></li>
<li><a href="#how-to-create-seven-classic-games-with-ania-kubow">스페이스 인베이더 게임 만들기</a></li>
<li><a href="#how-to-create-seven-classic-games-with-ania-kubow">프로거 게임 만들기</a></li>
<li><a href="#how-to-create-seven-classic-games-with-ania-kubow">테트리스 게임 만들기</a></li>
</ol>
<h2 id="">리액트 프로젝트</h2>
<ol>
<li><a href="#how-to-build-a-tic-tac-toe-game-using-react-hooks">리액트 훅을 사용해 틱택토 게임 만들기</a></li>
<li><a href="#how-to-build-a-tetris-game-using-react-hooks">리액트 훅을 사용해 테트리스 게임 만들기</a></li>
<li><a href="#how-to-create-a-birthday-reminder-app">생일 알림 앱 만들기</a></li>
<li><a href="#how-to-create-a-tours-page">여행 페이지 만들기</a></li>
<li><a href="#how-to-create-an-accordion-menu">아코디언 메뉴 만들기</a></li>
<li><a href="#how-to-create-tabs-for-a-portfolio-page">포트폴리오 만들기</a></li>
<li><a href="#how-to-create-a-review-slider">리뷰 슬라이더 만들기</a></li>
<li><a href="#how-to-create-a-color-generator">컬러 제너레이터 만들기</a></li>
<li><a href="#how-to-create-a-stripe-payment-menu-page">Stripe 결제 페이지 만들기</a></li>
<li><a href="#how-to-create-a-shopping-cart-page">장바구니 페이지 만들기</a></li>
<li><a href="#how-to-create-a-cocktail-search-page">칵테일 검색 페이지 만들기</a></li>
</ol>
<h2 id="">타입스크립트 프로젝트</h2>
<ol>
<li><a href="#how-to-build-a-quiz-app-with-react-and-typescript">리액트와 타입스크립트로 퀴즈 앱 만들기</a></li>
<li><a href="#how-to-create-an-arkanoid-game-with-typescript">타입스크립트로 알카노이드 게임 만들기</a></li>
</ol>
<h2 id="">바닐라 자바스크립트 프로젝트</h2>
<p>아직 자바스크립트 기초 내용이 낯설다면, 프로젝트를 진행하기 전 이 <a href="https://www.youtube.com/watch?v=PkZNo7MFNFg">강의</a>를 먼저 보는 것을 추천드립니다.</p>
<p>아래 대부분의 스크린샷은 <a href="https://www.vanillajavascriptprojects.com/">이곳</a>에서 확인 가능합니다.</p>
<h3 id="flipper">Color Flipper 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/color-flipper.png" alt="color flipper" width="600" height="400" loading="lazy"></p>
<p><a href="https://www.youtube.com/watch?v=3PHXvlpOkf4&amp;t=421s">John Smilga의 튜토리얼</a>에서는 랜덤으로 배경색을 바꿀 수 있는 Color Flipper를 어떻게 만드는지 설명하고 있습니다. Color Flipper는 DOM을 처음 연습하기에 좋은 프로젝트입니다.</p>
<p>DOM을 공부해야 하는 이유에 대해 <a href="https://www.freecodecamp.org/news/whats-the-document-object-model-and-why-you-should-know-how-to-use-it-1a2d0bc5429d/#:~:text=Advantages,the%20page%20without%20a%20refresh.">Leonardo Maldonado의 글</a>에서는 이렇게 설명하고 있습니다.</p>
<blockquote>
<p>DOM을 다루는 방법을 알게 되는 순간 무한한 가능성이 열립니다. 새로고침을 하지 않아도 페이지에서 데이터를 업데이트해 주는 애플리케이션을 만들 수 있습니다. 사용자가 커스터마이즈 할 수 있는 애플리케이션을 만들 수도 있고, 새로고침 없이 페이지의 레이아웃을 변경할 수도 있습니다.<br>
이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
</blockquote>
<ul>
<li>arrays</li>
<li>document.getElementById()</li>
<li>document.querySelector()</li>
<li>addEventListener()</li>
<li>document.body.style.backgroundColor</li>
<li>Math.floor()</li>
<li>Math.random()</li>
<li>array.length</li>
</ul>
<p>시작하기 앞서 John이 모든 프로젝트의 기본 파일에 액세스하는 방법을 설명해 주는 이 <a href="https://www.youtube.com/watch?v=3PHXvlpOkf4&amp;t=0s">영상</a>을 먼저 보는 걸 추천합니다.</p>
<h3 id="counter">카운터 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/counter.png" alt="카운터" width="600" height="400" loading="lazy"></p>
<p>John Smilga의 <a href="https://www.youtube.com/watch?v=3PHXvlpOkf4&amp;t=421s">튜토리얼</a>에서는 양수인지 음수인지에 따라 색상이 바뀌는 카운터를 만드는 방법에 대해 설명하고 있습니다.</p>
<p>이 프로젝트를 통해 DOM을 더 연습해 볼 수 있으며, 뽀모도로 시계와 같은 다른 프로젝트에서도 이 간단한 카운터를 사용할 수 있을 것입니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>document.querySelectorAll()</li>
<li>forEach()</li>
<li>addEventListener()</li>
<li>currentTarget 속성</li>
<li>classList</li>
<li>textContent</li>
</ul>
<h3 id="how-to-create-a-review-carousel">리뷰 캐러셀 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/reviews.png" alt="리뷰 캐러셀" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=3PHXvlpOkf4&amp;t=2644s">튜토리얼</a>에서는 버튼을 누르면 랜덤으로 리뷰를 보여주는 리뷰 캐러셀을 만드는 방법에 대해 설명합니다.</p>
<p>이 기능은 고객 후기를 보여주는 전자 상거래 사이트나 개인 포트폴리오에서 유용하게 쓰일 수 있습니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>객체</li>
<li>DOMContentLoaded</li>
<li>addEventListener()</li>
<li>array.length</li>
<li>textContent</li>
</ul>
<h3 id="how-to-create-a-responsive-navbar">반응형 내비게이션 바 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/navbar-1.png" alt="내비게이션 바" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=3PHXvlpOkf4&amp;t=4289s">튜토리얼</a>에서는 작은 전자 기기에서 햄버거 메뉴로 바뀌는 반응형 내비게이션 바를 만드는 법에 대해 소개하고 있습니다.</p>
<p>웹 개발자에게 반응형 웹 사이트를 만들 줄 아는 것은 무척 중요합니다. 이 기능은 많은 웹 사이트에서 사용되고 있습니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>document.querySelector()</li>
<li>addEventListener()</li>
<li>classList.toggle()</li>
</ul>
<h3 id="how-to-create-a-sidebar">사이드바 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/sidebar.png" alt="사이드바" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=3PHXvlpOkf4&amp;t=5181s">튜토리얼</a>에서는 애니메이션과 함께 사이드바를 만드는 방법에 대해 소개하고 있습니다.</p>
<p>개인 웹 사이트에 추가할 수 있는 아주 멋진 기능입니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>document.querySelector()</li>
<li>addEventListener()</li>
<li>classList.toggle()</li>
<li>classList.remove()</li>
</ul>
<h3 id="how-to-create-a-modal">모달 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/modal.png" alt="모달 창" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=3PHXvlpOkf4&amp;t=5943s">튜토리얼</a>에서는 사용자들이 웹 사이트에서 특정한 무언가를 하거나 볼 때 사용되는 모달창을 만드는 법에 대해 소개합니다.</p>
<p>모달 창이 사용되는 좋은 예시는 사용자가 사이트에서 무언가를 변경하고 저장하지 않은 채 다른 페이지로 이동할 때의 경우입니다. 변경 사항을 저장하지 않으면 정보가 삭제될 수 있다고 경고하는 모달 창을 만들 수 있는 것이죠.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>document.querySelector()</li>
<li>addEventListener()</li>
<li>classList.add()</li>
<li>classList.remove()</li>
</ul>
<h3 id="how-to-create-a-faq-page">FAQ 페이지 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/FAQ-section.png" alt="FAQ 페이지" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=3PHXvlpOkf4&amp;t=6506s">튜토리얼</a>에서는 FAQ(자주 묻는 질문) 페이지를 만드는 방법에 대해 소개합니다. 사용자들에게 사업 내용을 알려주고 유기적인 검색 결과를 통해 웹 사이트로 유입시키고자 할 때 사용됩니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>document.querySelectorAll()</li>
<li>addEventListener()</li>
<li>forEach()</li>
<li>classList.remove()</li>
<li>classList.toggle()</li>
</ul>
<h3 id="how-to-create-a-restaurant-menu-page">식당 메뉴판 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/menu.png" alt="식당 메뉴판" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=3PHXvlpOkf4&amp;t=8185s">튜토리얼</a>에서는 레스토랑의 메뉴를 필터링해주는 페이지를 만드는 법에 대해 소개합니다. 재미있는 이 프로젝트를 통해 map, reduce, filter와 같은 고차 함수에 대해서도 학습할 수 있습니다.</p>
<p>Yazeed Bzadough는 <a href="https://www.freecodecamp.org/news/a-quick-intro-to-higher-order-functions-in-javascript-1a014f89c6b/">이 글</a>에서 고차 함수를 다음과 같이 언급하고 있습니다.</p>
<blockquote>
<p>고차 함수의 가장 큰 이점은 엄청난 재사용성에 있습니다.<br>
이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
</blockquote>
<ul>
<li>배열</li>
<li>객체</li>
<li>forEach()</li>
<li>DOMContentLoaded</li>
<li>map, reduce, filter</li>
<li>innerHTML</li>
<li>includes 메서드</li>
</ul>
<h3 id="how-to-create-a-video-background">비디오 배경 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/video-1.png" alt="비디오 배경" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=3PHXvlpOkf4&amp;t=11773s">튜토리얼</a>에서는 play, pause 기능을 이용해 영상 배경을 만드는 방법에 대해서 소개하고 있습니다. 많은 웹 사이트에서 찾아볼 수 있는 기능이죠.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>document.querySelector()</li>
<li>addEventListener()</li>
<li>classList.contains()</li>
<li>classList.add()</li>
<li>classList.remove()</li>
<li>play()</li>
<li>pause()</li>
</ul>
<h3 id="how-to-create-a-navigation-bar-on-scroll">스크롤바를 따라 움직이는 내비게이션 바 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/color-flipper.png" alt="내비게이션 바" width="600" height="400" loading="lazy"></p>
<p>이번 <a href="https://www.youtube.com/watch?v=3PHXvlpOkf4&amp;t=12765s">튜토리얼</a>에서는 스크롤 할 때 내려가다가 특정 높이에서 멈추는 내비게이션 바를 구현하고 있습니다.</p>
<p>많은 전문 웹 사이트에서 사용하고 있는 인기 기능입니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>document.getElementById()</li>
<li>getFullYear()</li>
<li>getBoundingClientRect()</li>
<li>slice 메서드</li>
<li>window.scrollTo()</li>
</ul>
<h3 id="how-to-create-tabs-that-display-different-content">서로 다른 내용을 보여주는 탭 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/tabs.png" alt="히스토리 탭" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=3PHXvlpOkf4&amp;t=16575s">튜토리얼</a>은 서로 다른 내용을 보여주는 탭을 만드는 방법에 대해 소개하고 있습니다. 싱글 페이지 애플리케이션(SPA)을 만들 때 유용한 기능이죠.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>classList.add()</li>
<li>classList.remove()</li>
<li>forEach()</li>
<li>addEventListener()</li>
</ul>
<h3 id="how-to-create-a-countdown-clock">카운트다운 시계 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/countdown.png" alt="카운트다운 시계" width="600" height="400" loading="lazy"></p>
<p>이번 <a href="https://www.youtube.com/watch?v=3PHXvlpOkf4&amp;t=17933s">튜토리얼</a>은 카운트다운 시계를 만드는 방법에 대해 소개합니다. 전자 상거래 사이트에서 새로운 제품이 출시되거나 할인이 종료될 때 사용될 수 있는 기능이죠.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>getFullYear()</li>
<li>getMonth()</li>
<li>getDate()</li>
<li>Math.floor()</li>
<li>setInterval()</li>
<li>clearInterval()</li>
</ul>
<h3 id="how-to-create-your-own-lorem-ipsum">나만의 Lorem ipsum 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/lorem-ipsum.png" alt="lorem ipsum 생성기" width="600" height="400" loading="lazy"></p>
<p>이번 <a href="https://www.youtube.com/watch?v=3PHXvlpOkf4&amp;t=21395s">튜토리얼</a>에서는 나만의 Lorem ipsum을 만드는 방법에 대해 배울 수 있습니다.</p>
<p>Lorem ipsum은 웹 사이트에서 사용할 수 있는 더미 텍스트입니다. 이 프로젝트를 통해 여러분의 창의성을 뽐내고 자신만의 더미 텍스트를 만들어 보세요.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>parseInt()</li>
<li>Math.floor()</li>
<li>Math.random()</li>
<li>isNaN()</li>
<li>slice 메서드</li>
<li>event.preventDefault()</li>
</ul>
<h3 id="how-to-create-a-grocery-list">식료품 리스트 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/grocery-list.png" alt="식료품 리스트" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=3PHXvlpOkf4&amp;t=22703s">튜토리얼</a>에서는 장바구니 리스트에서 아이템을 추가하고 삭제할 수 있는 방법과 간단한 CRUD(Create, Read, Update, Delete) 앱을 만드는 방법에 대해 소개합니다.</p>
<p>CRUD는 풀스택 애플리케이션에서 굉장히 중요한 부분을 차지합니다. 이 기능이 없으면 여러분은 SNS에서 게시글을 수정하거나 삭제하는 것과 같은 일을 할 수 없을 것입니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>DOMContentLoaded</li>
<li>new Date()</li>
<li>createAttribute()</li>
<li>setAttribute()</li>
<li>appendChild()</li>
<li>filter()</li>
<li>map()</li>
</ul>
<h3 id="how-to-create-an-image-slider">이미지 슬라이더 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/image-slider.png" alt="이미지 슬라이더" width="600" height="400" loading="lazy"></p>
<p>이번 <a href="https://www.youtube.com/watch?v=3PHXvlpOkf4&amp;t=28874s">튜토리얼</a>에서는 어떤 사이트에도 추가 가능한 이미지 슬라이더를 만드는 방법에 대해 소개합니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>querySelectorAll()</li>
<li>addEventListener()</li>
<li>forEach()</li>
<li>if/else statements</li>
</ul>
<h3 id="how-to-create-a-rock-paper-scissors-game">가위바위보 게임 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/rock-paper-scissors.png" alt="가위바위보 게임" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=jaVNP3nIAv0">튜토리얼</a>에서는 Tenzin이 가위바위보 게임을 만드는 방법을 소개합니다. DOM 연습을 추가적으로 해볼 수 있는 재미있는 프로젝트입니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>addEventListener()</li>
<li>Math.floor()</li>
<li>Math.random()</li>
<li>switch문</li>
</ul>
<h3 id="how-to-create-a-simon-game">사이먼 게임 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/simon-game.png" alt="사이먼 게임" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=n_ec3eowFLQ">튜토리얼</a>에서는 Beau Carnes가 사이먼 게임을 만드는 방법에 대해 소개합니다. 게임 속 다양한 요소들과 각각의 기능을 구현하는 방법에 대해 생각해 볼 수 있는 좋은 프로젝트입니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>querySelector()</li>
<li>addEventListener()</li>
<li>setInterval()</li>
<li>clearInterval()</li>
<li>setTimeout()</li>
<li>play()</li>
<li>Math.floor()</li>
<li>Math.random()</li>
</ul>
<h3 id="how-to-create-a-platformer-game">플랫포머 게임 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/platformer-game.png" alt="플랫포머 게임" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=w-OKdSHRlfA">튜토리얼</a>에서는 Frank Poth가 플랫포머 게임을 만드는 방법에 대해 소개합니다. 이번 프로젝트를 통해 객체 지향 프로그래밍의 원칙과 모델, 뷰, 컨트롤러로 구성된 소프트웨어 패턴(MVC Pattern)에 대해 학습할 수 있습니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>this 키워드</li>
<li>for 반복문</li>
<li>switch문</li>
<li>OOP 원칙</li>
<li>MVC 패턴</li>
<li>캔버스 API</li>
</ul>
<h3 id="how-to-create-doodle-jump-and-flappy-bird">두들 점프와 플래피 버드 게임 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/doodle-jump.png" alt="JS 튜토리얼 썸네일" width="600" height="400" loading="lazy"></p>
<p>Ania Kubox는 이 <a href="https://www.youtube.com/watch?v=8xPsg6yv7TU&amp;t=0s">영상 시리즈</a>에서 두들 점프와 플래피 버드 게임을 만드는 방법에 대해 소개합니다.</p>
<p>게임을 만들어보면 자바스크립트를 재미있게 학습하면서 자주 쓰이는 자바스크립트 메서드를 연습할 수 있습니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>createElement()</li>
<li>forEach()</li>
<li>setInterval()</li>
<li>clearInterval()</li>
<li>removeChild()</li>
<li>appendChild()</li>
<li>addEventListener()</li>
<li>removeEventListener()</li>
</ul>
<h3 id="how-to-create-seven-classic-games-with-ania-kubow">Ania Kubow와 7개의 자바스크립트 게임 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/7-js-games.png" alt="Ania Kubow" width="600" height="400" loading="lazy"></p>
<p>Ania Kybow는 이 <a href="https://www.youtube.com/watch?v=lhNdUVh3qCc">강의</a>에서 재미있는 게임 일곱가지를 소개하고 있습니다.</p>
<ol>
<li><a href="https://www.youtube.com/watch?v=lhNdUVh3qCc&amp;t=115s">메모리 게임</a></li>
<li><a href="https://www.youtube.com/watch?v=lhNdUVh3qCc&amp;t=699s">두더지 잡기</a></li>
<li><a href="https://www.youtube.com/watch?v=lhNdUVh3qCc&amp;t=1187s">커넥트 포 게임</a></li>
<li><a href="https://www.youtube.com/watch?v=lhNdUVh3qCc&amp;t=1657s">뱀 게임</a></li>
<li><a href="https://www.youtube.com/watch?v=lhNdUVh3qCc&amp;t=2590s">스페이스 인베이더</a></li>
<li><a href="https://www.youtube.com/watch?v=lhNdUVh3qCc&amp;t=3546s">프로거 게임</a></li>
<li><a href="https://www.youtube.com/watch?v=lhNdUVh3qCc&amp;t=4778s">테트리스</a></li>
</ol>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>for 반복문</li>
<li>onclick 이벤트</li>
<li>화살표 함수</li>
<li>sort()</li>
<li>pop()</li>
<li>unshift()</li>
<li>push()</li>
<li>indexOf()</li>
<li>includes()</li>
<li>splice()</li>
<li>concat()</li>
</ul>
<h2 id="">리액트 프로젝트</h2>
<p>아직 리액트 기초 내용이 익숙하지 않다면 프로젝트를 진행하기 전에 이 <a href="https://www.youtube.com/watch?v=4UZrsTqkcW4">강의</a>를 먼저 듣는 것을 추천합니다.</p>
<h3 id="how-to-build-a-tic-tac-toe-game-using-react-hooks">리액트 훅을 사용해 틱택토 게임 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/tic-tac-game-1.png" alt="틱택토 썸네일" width="600" height="400" loading="lazy"></p>
<p>Per Harald Borgen는 이 <a href="https://www.freecodecamp.org/news/learn-how-to-build-tic-tac-toe-with-react-hooks/">프리코드캠프 글</a>에서 Thomas Weibenfalk가 제공한 틱택토 게임 튜토리얼에 대해 설명하고 있습니다. Scrimba의 유튜브 채널에서 <a href="https://www.youtube.com/watch?v=Z5RbPrK4VqM">영상 강의</a>를 확인해보세요.</p>
<p>리액트 기초를 익히고 훅을 연습하기에 좋은 프로젝트입니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>useState()</li>
<li>import / export</li>
<li>JSX</li>
</ul>
<h3 id="how-to-build-a-tetris-game-using-react-hooks">리액트 훅을 사용해 테트리스 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/react-tetris-1.png" alt="테트리스 게임" width="600" height="400" loading="lazy"></p>
<p>이번 <a href="https://www.youtube.com/watch?v=ZGOaCxX8HIU">튜토리얼</a>에서는 Thomas Weibenfalk가 리액트 훅과 styled components를 사용해 테트리스 게임을 구현합니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>useState()</li>
<li>useEffect()</li>
<li>useRef()</li>
<li>useCallback()</li>
<li>styled components</li>
</ul>
<h3 id="how-to-create-a-birthday-reminder-app">생일 알림 앱 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/brithday-app.png" alt="생일 알림 앱" width="600" height="400" loading="lazy"></p>
<p>John Smilga의 <a href="https://www.youtube.com/watch?v=a_7Z7C_JCyo&amp;t=438s">강의</a>에서는 생일 알림 앱을 만드는 방법에 대해 학습할 수 있습니다. 리액트 기초를 학습하고 훅을 연습하기에 좋은 프로젝트입니다.</p>
<p>프로젝트 파일 설정을 위해 먼저 John의 <a href="https://www.youtube.com/watch?v=a_7Z7C_JCyo&amp;t=214s">영상</a>을 보는 것을 추천합니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>useState()</li>
<li>import / export</li>
<li>JSX</li>
<li>map()</li>
</ul>
<h3 id="how-to-create-a-tours-page">여행 페이지 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/tours-page.png" alt="루브르 박물관" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=a_7Z7C_JCyo&amp;t=1181s">튜토리얼</a>에서는 사용자가 관심 없는 여행지를 삭제할 수 있는 여행 사이트를 만드는 방법에 대해 소개합니다.</p>
<p>이 프로젝트를 통해 리액트 훅과 async/await 문법을 연습할 수 있습니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>try...catch문</li>
<li>async/await</li>
<li>useEffect()</li>
<li>useState()</li>
</ul>
<h3 id="how-to-create-an-accordion-menu">아코디언 메뉴 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/accordion-react.png" alt="아코디언 메뉴" width="600" height="400" loading="lazy"></p>
<p>이번 <a href="https://www.youtube.com/watch?v=a_7Z7C_JCyo&amp;t=4642s">튜토리얼</a>에서는 Q&amp;A 아코디언을 만드는 방법에 대해 설명합니다. 사용자에게 혁신적인 방법으로 콘텐츠를 보여줄 때 유용한 방식입니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>리액트 아이콘</li>
<li>useState()</li>
<li>map()</li>
</ul>
<h3 id="how-to-create-tabs-for-a-portfolio-page">포트폴리오 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/tabs-portfolio-page-react.png" alt="포트폴리오" width="600" height="400" loading="lazy"></p>
<p>이번 <a href="https://www.youtube.com/watch?v=a_7Z7C_JCyo&amp;t=6726s">튜토리얼</a>에서는 가짜 포트폴리오 웹 페이지에서 탭을 만드는 방법에 대해 설명합니다. 탭은 싱글 페이지 애플리케이션에서 서로 다른 내용을 보여줄 때 유용하게 사용됩니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>async/await</li>
<li>리액트 아이콘</li>
<li>useEffect()</li>
<li>useState()</li>
</ul>
<h3 id="how-to-create-a-review-slider">리뷰 슬라이더 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/react-slider.png" alt="리뷰 슬라이더" width="600" height="400" loading="lazy"></p>
<p>이번 <a href="https://www.youtube.com/watch?v=a_7Z7C_JCyo&amp;t=8020s">튜토리얼</a>에서는 몇 초마다 새로운 리뷰를 보여주는 리뷰 슬라이더를 만드는 법에 대해 설명합니다.</p>
<p>리뷰 슬라이더는 전자 상거래 사이트나 포트폴리오에 넣을 수 있는 훌륭한 기능입니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>리액트 아이콘</li>
<li>useEffect()</li>
<li>useState()</li>
<li>map()</li>
</ul>
<h3 id="how-to-create-a-color-generator">컬러 제너레이터 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/react-color-generator.png" alt="컬러 제너레이터" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=a_7Z7C_JCyo&amp;t=11329s">튜토리얼</a>을 통해 컬러 제너레이터를 만드는 방법에 대해서 학습할 수 있습니다. 훅과 setTimeout 함수를 연습하기에 좋은 프로젝트입니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>setTimeout()</li>
<li>clearTimeout()</li>
<li>useEffect()</li>
<li>useState()</li>
<li>try...catch문</li>
<li>event.preventDefault()</li>
</ul>
<h3 id="how-to-create-a-stripe-payment-menu-page">Stripe 결제 페이지 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/stripe-page.png" alt="Stripe의 결제 페이지" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=a_7Z7C_JCyo&amp;t=20686s">튜토리얼</a>에서는 Stripe 사의 결제 페이지를 구현하는 방법에 대해 설명합니다. 리액트 컴포넌트를 사용해 상품 랜딩 페이지를 디자인하는 방법을 연습해보기 좋은 프로젝트입니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>리액트 아이콘</li>
<li>useRef()</li>
<li>useEffect()</li>
<li>useState()</li>
<li>useContext()</li>
</ul>
<h3 id="how-to-create-a-shopping-cart-page">장바구니 페이지 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/shopping-cart-page.png" alt="장바구니 페이지" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=a_7Z7C_JCyo&amp;t=24115s">튜토리얼</a>에서는 상품을 추가하고 삭제할 수 있는 장바구니 페이지를 구현합니다. <code>useReducer</code> 훅을 맛보기에 좋은 프로젝트가 될 것입니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>map()</li>
<li>filter()</li>
<li><code>&lt;svg&gt;</code> 요소</li>
<li>useReducer()</li>
<li>useContext()</li>
</ul>
<h3 id="how-to-create-a-cocktail-search-page">칵테일 검색 페이지 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/cocktails-page.png" alt="칵테일 검색 페이지" width="600" height="400" loading="lazy"></p>
<p>이 <a href="https://www.youtube.com/watch?v=a_7Z7C_JCyo&amp;t=27597s">튜토리얼</a>에서는 칵테일 검색 페이지를 구현합니다. 이 프로젝트를 통해 리액트 라우터 사용 방법에 대해 익힐 수 있습니다.</p>
<p>리액트 라우터는 웹 사이트 내에서 이동하고 소개 페이지 혹은 연락 페이지와 같은 다른 컴포넌트를 보여줄 수 있도록 해주는 기능입니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li><code>&lt;Router&gt;</code></li>
<li><code>&lt;Switch&gt;</code></li>
<li>useCallback()</li>
<li>useContext()</li>
<li>useEffect()</li>
<li>useState()</li>
</ul>
<h2 id="">타입스크립트 프로젝트</h2>
<h3 id="how-to-build-a-quiz-app-with-react-and-typescript">리액트와 타입스크립트로 퀴즈 앱 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/quiz-app.png" alt="퀴즈 앱" width="600" height="400" loading="lazy"></p>
<p>Thomas Weibenfalk는 이 <a href="https://www.youtube.com/watch?v=F2JCjVSZlG0">튜토리얼</a>에서 리액트와 타입스크립트로 퀴즈 앱을 만드는 방법에 대해 소개합니다. 타입스크립트의 기본 내용을 학습하기에 좋은 프로젝트입니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>React.FC</li>
<li>styled components</li>
<li>dangerouslySetInnerHTML</li>
</ul>
<h3 id="how-to-create-an-arkanoid-game-with-typescript">타입스크립트로 알카노이드 게임 만들기</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2021/03/akrnoid-game.png" alt="알카노이드 게임" width="600" height="400" loading="lazy"></p>
<p>이번 <a href="https://www.youtube.com/watch?v=7bejSTim38A">튜토리얼</a>에서는 타입스크립트로 클래식 게임인 알카노이드 게임을 만드는 방법에 대해 설명합니다. 이번 프로젝트 역시 타입스크립트의 기본 내용을 학습하기에 좋습니다.</p>
<p>이 프로젝트에서 다루는 주요 개념은 다음과 같습니다:</p>
<ul>
<li>타입</li>
<li>클래스</li>
<li>모듈</li>
<li>HTMLCanvasElement</li>
</ul>
<p>지금까지 바닐라 자바스크립트, 리액트, 타입스크립트로 해볼 수 있는 40가지의 프로젝트를 소개해 보았습니다. 도움이 되었길!</p>
<p>즐거운 코딩하세요!</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
