<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>me-hana.log</title>
        <link>https://velog.io/</link>
        <description>프론트 어쩌고</description>
        <lastBuildDate>Thu, 10 Apr 2025 01:59:41 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>me-hana.log</title>
            <url>https://velog.velcdn.com/images/me-hana/profile/928bc88b-0e71-4d28-8cea-677e0bc473a7/image.jfif</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. me-hana.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/me-hana" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[React로 직접 포트폴리오 페이지를 만들기 위한 github.io 배포]]></title>
            <link>https://velog.io/@me-hana/React%EB%A1%9C-%EC%A7%81%EC%A0%91-%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%A5%BC-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%9C%84%ED%95%9C-github.io-%EB%B0%B0%ED%8F%AC</link>
            <guid>https://velog.io/@me-hana/React%EB%A1%9C-%EC%A7%81%EC%A0%91-%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%A5%BC-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%9C%84%ED%95%9C-github.io-%EB%B0%B0%ED%8F%AC</guid>
            <pubDate>Thu, 10 Apr 2025 01:59:41 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>개발자 친구들이랑 &#39;깃친&#39; 맺으려고 아이디깠다가 친구가 갑자기 &quot;어디 구경 좀 하까~ㅋ&quot; 라면서 주소창에 <code>내아이디.github.io</code> 넣어본 경험 있는 사람? 뭐, 없음 말구 ㅎㅎ
난 있으니까 추후 또 그런 일을 대비해서 그럴듯한 포폴페이지를 만들어 볼거다.</p>
</blockquote>
<h2 id="😎-githubio">😎 github.io?</h2>
<p>GitHub에서 제공하는 ‘GitHub Pages’라는 웹 호스팅 서비스가 있는데, 얘네가 제공해주는 도메인 주소 형식이 <code>github.io</code>다.</p>
<p>Public 으로 설정된 Repository, 정적 페이지에 한하여 해당 서비스를 제공해준다.</p>
<p>보통은 템플릿을 사용해 꾸미지만, 남들과 다른 나만의 페이지를 갖고 싶다거나, 프론트엔드 개발자라면 직접 꾸며봐도 좋은 경험이 될 것이다.</p>
<h2 id="😎-github-pages-호스팅-설정">😎 GitHub Pages 호스팅 설정</h2>
<p>페이지를 꾸미는 건 천천히 하고, 일단 페이지부터 호스팅하고 배포하는 방법부터 알아보자.
그래야 꾸미지.</p>
<h3 id="1-githubio-레포지토리-만들기">1. github.io 레포지토리 만들기</h3>
<p><img src="https://velog.velcdn.com/images/me-hana/post/219959df-e913-474e-922e-f502487b7673/image.PNG" alt=""></p>
<p>사실 레포지토리 이름은 아무거나 해도 상관없다.
대신 도메인은 <code>사용자이름.github.io/레포지토리 이름</code> 꼴이 될거다.</p>
<p>그런데 레포지토리 이름을 <code>사용자이름.github.io</code> 로 설정하면 그 자체가 도메인 주소가 된다.
완전 상징적이지? 그래서 다들 <code>사용자이름.github.io</code> 이라는 레포지토리를 포트폴리오 페이지로 사용한다.</p>
<p><strong>─정리─</strong></p>
<ul>
<li>레포지토리 이름은 사용자이름.github.io 로 설정</li>
<li>꼭! Public 으로 설정</li>
<li>README file은 체크하든.. 말든.. 상관없지만 그냥 페이지 확인용으로 체크</li>
</ul>
<h3 id="2-내-페이지가-잘-보이는-지-확인하려면">2. 내 페이지가 잘 보이는 지 확인하려면</h3>
<p><img src="https://velog.velcdn.com/images/me-hana/post/fdb725a9-5116-4f52-a652-294c12b511b6/image.png" alt=""></p>
<p>내 레포지토리가 페이지화 되어 잘 서비스 되고 있는지 확인하는 과정이다.
Your site is live at &quot;https://사용자이름.github.io&quot; 라고 써있는데,
↑ 이게 이제 내 포트폴리오 페이지의 도메인이다.</p>
<p><strong>─정리─</strong></p>
<ul>
<li><code>사용자이름.github.io</code> 레포지토리 &gt; Settings &gt; Pages 로 들어가기</li>
<li>README.md 를 생성했다면, 해당 URL에 접속했을 때 README.md 내용이 보임</li>
</ul>
<h3 id="3-react-프로젝트-생성하기">3. React 프로젝트 생성하기</h3>
<p><img src="https://velog.velcdn.com/images/me-hana/post/852233b6-2a81-45f5-8561-fe7e0d334192/image.png" alt=""></p>
<p>내 레포지토리를 clone 하기 위해 HTTPS 주소를 복사한 다음,</p>
<p><img src="https://velog.velcdn.com/images/me-hana/post/2ec02e44-b6ff-44df-9577-7b683adfed12/image.PNG" alt=""></p>
<p>내가 원하는 위치에 가서 터미널을 열고, <code>git clone 복사한주소</code> 명령어를 입력하면, <code>사용자이름.github.io</code> 라는 폴더가 생겼을 것이다.
그 폴더의 위치에서 터미널을 열고, 다음 명령어들을 입력해서 React 프로젝트를 생성한다.</p>
<p><strong>─따라해─</strong>
0. <code>npm install -g create-react-app</code>
: 이전에 CRA를 global로 설치하지 않았던 사람만 입력
1.<code>npx create-react-app ./</code>
: 현재 폴더에 CRA 프로젝트 생성. 앞에서 README.md를 추가한 사람은 README.md 파일이 README.old.md라는 파일로 바뀌었을 것이다. 걍 삭제하삼.
2. <code>npm install gh-pages --save-dev</code>
: GitHub Pages 배포를 도와주는 gh-pages 패키지 설치. 
3. gh-pages를 사용하기 위해 package.json 파일에 다음 내용을 추가</p>
<pre><code class="language-javascript">&quot;homepage&quot;: &quot;http://ha-yeon-lee.github.io&quot;,
// ...(생략)
&quot;scripts&quot;: {
    &quot;predeploy&quot;: &quot;npm run build&quot;,
    &quot;deploy&quot;: &quot;gh-pages -d build&quot;,
    // ...(생략)
}
// ...(생략)</code></pre>
<h3 id="4-react-프로젝트를-githubio에-배포하기">4. React 프로젝트를 github.io에 배포하기</h3>
<p><img src="https://velog.velcdn.com/images/me-hana/post/6f29dbeb-c808-4708-a7fc-dc2aafd33be1/image.png" alt="">
gh-pages는 내 레포지토리에 gh-pages라는 브랜치를 자동으로 생성하고, 그 브랜치를 도메인에 반영하므로 위 사진처럼 설정해두어야 한다.
이제 터미널에서 <code>npm run deploy</code>만 입력하면 내 포트폴리오 페이지에 반영될 것이다.
프로젝트를 수정하고 나서, <code>npm run deploy</code> 만 입력하면 배포된다.</p>
<p>단, 시간 당 10회까지만 가능하므로 너무 잦은 배포는 주의.
반영에 시간이 좀 걸릴 수도 있으므로, 5분 정도는 여유를 두는 것도 기억해둘 것.</p>
<p><strong>─정리─</strong></p>
<ul>
<li><code>사용자이름.github.io</code> 레포지토리 &gt; Settings &gt; Pages &gt; Build and deployment 에서 기본 브랜치를 gh-pages로 설정</li>
<li>프로젝트 수정이 끝나면, <code>npm run deploy</code> 명령어로 배포</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS로 코딩테스트 준비하기] 백준(BOJ) 입력받기]]></title>
            <link>https://velog.io/@me-hana/JS%EB%A1%9C-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A4%80%EB%B9%84%ED%95%98%EA%B8%B0-%EB%B0%B1%EC%A4%80BOJ-%EC%9E%85%EB%A0%A5%EB%B0%9B%EA%B8%B0</link>
            <guid>https://velog.io/@me-hana/JS%EB%A1%9C-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A4%80%EB%B9%84%ED%95%98%EA%B8%B0-%EB%B0%B1%EC%A4%80BOJ-%EC%9E%85%EB%A0%A5%EB%B0%9B%EA%B8%B0</guid>
            <pubDate>Thu, 11 Aug 2022 13:21:58 GMT</pubDate>
            <description><![CDATA[<h3 id="😎-서론">😎 서론</h3>
<p>Javascript는 APS에서는 확실히 주류가 아니다. Javascript로 코딩테스트 공부를 해야겠다고 마음 먹은 순간부터 느낄 수 있다. 입력부터 막막하거든!</p>
<p>기업에서 주로 채택하고 있는 <strong>프로그래머스(Programmers)</strong> 에서는 Javascript 문제 풀이 환경이 잘 되어있지만, 최근 기업용 코딩테스트 플랫폼으로의 채택률이 커지고 있는 <strong>구름(goorm)</strong> 이나 국내 최대 APS 사이트인 <strong>백준(BOJ)</strong> 에서는 입력을 조금 신경써줘야 한다.</p>
<p>일단 백준 입력 방법부터 정리한 뒤, 추후에 구름 입력 방법을 추가하려고 한다.</p>
<h3 id="😎-백준">😎 백준</h3>
<p>채점 언어를 Javascript가 아닌, <strong>Node.js</strong>로 선택한다. Javascript에서는 입력을 받을 때 readline 모듈과 fs 모듈을 사용하는데, 백준에서는 <strong>fs 모듈 방식</strong>을 권장하고 있다. (readline은 fs에 비해 다양한 내부 메서드가 있지만, 비교적 느리다고 함) </p>
<h4 id="한-줄-입력-받기">한 줄 입력 받기</h4>
<pre><code class="language-javascript">const fs = require(&#39;fs&#39;);
let input = fs.readFileSync(&#39;/dev/stdin&#39;).toString().split(&#39; &#39;);

console.log(input);</code></pre>
<h4 id="여러-줄-입력-받기">여러 줄 입력 받기</h4>
<pre><code class="language-javascript">let fs = require(&#39;fs&#39;);
let input = fs.readFileSync(&#39;/dev/stdin&#39;).toString().split(&#39;\n&#39;);

let count = input[0];
let numbers = [];

for (let i = 1; i &lt; input.length; i++) {
  if (input[i] !== &#39;&#39;) {
    numbers.push(input[i].split(&#39; &#39;));
  }
}

for (let i = 0; i &lt; numbers.length; i++){
  let num1 = Number(numbers[i][0]);
  let num2 = Number(numbers[i][1]);

  console.log(num1 + num2);
}</code></pre>
<p>핵심은 .split()의 argument로 들어가는 게 <strong>공백이냐, \n</strong>이냐 다.</p>
<h4 id="백준-채점이-아닌-vscode에서-실행할-때">백준 채점이 아닌, VSCode에서 실행할 때</h4>
<ol>
<li>터미널에서 <code>npm install fs</code> 명령어 실행</li>
<li>폴더 내, 원하는 위치에 input.txt 파일 생성 (테스트 케이스가 들어가는 파일)</li>
<li>위의 코드에서 <code>fs.readFileSync(&#39;/dev/stdin&#39;)</code> 대신 <code>fs.readFileSync(&#39;input.txt의 경로&#39;)</code> 입력 (단, 백준에 제출할 때는 <strong>다시 /dev/stdin 으로</strong> 바꿔줄 것)</li>
<li>콘솔창에 <code>node 알고리즘작성한js파일경로</code> 입력해서 내가 작성한 알고리즘 실행</li>
</ol>
<h3 id="😎-프로그래머스-참고">😎 프로그래머스 (참고)</h3>
<pre><code class="language-javascript">solution(input1){
  let answer = input1;

  return answer;
}</code></pre>
<p>프로그래머스에서는 사용자가 (입출력이 아닌) 알고리즘 로직 자체를 구현하는 데에 집중할 수 있도록 <strong>solution 함수</strong> 형태로 입력을 받고 있다. 난 solution 함수 내에서 로직을 구현하기만 하면 된다. 정말 편하다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS로 코딩테스트 준비하기] 프론트엔드 개발자의 코테 언어 선택]]></title>
            <link>https://velog.io/@me-hana/JS%EB%A1%9C-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A4%80%EB%B9%84%ED%95%98%EA%B8%B0-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%EC%BD%94%ED%85%8C-%EC%96%B8%EC%96%B4-%EC%84%A0%ED%83%9D</link>
            <guid>https://velog.io/@me-hana/JS%EB%A1%9C-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A4%80%EB%B9%84%ED%95%98%EA%B8%B0-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%EC%BD%94%ED%85%8C-%EC%96%B8%EC%96%B4-%EC%84%A0%ED%83%9D</guid>
            <pubDate>Wed, 10 Aug 2022 14:28:19 GMT</pubDate>
            <description><![CDATA[<h3 id="😎-서론">😎 서론</h3>
<p>난 프론트엔드 개발자지만, 코딩테스트는 Java와 Python으로 해왔다.
그런데 이직 준비를 하려고 찾아보니까 프론트엔드 직군은 Javascript Only로 보는 회사가 왜 이렇게 늘어난 건지.
사실 생각해보면, Javascript도 딱히 능숙하지도 않으면서 업무에 전혀 쓰이지 않는 Java, Python 공부를 해가며 코딩테스트 준비하는 게 어불성설 같긴 하다.</p>
<p>그래서 프론트엔드 개발자가 Javascript로 코딩테스트를 준비해야 하는 이유를 찾아보고 정리해본다.</p>
<h3 id="😎-javascript로-코테-준비하는-이유">😎 Javascript로 코테 준비하는 이유</h3>
<h4 id="1-프론트엔드-직군에서-javascript로만-테스트를-보는-경우가-많아졌다">1. 프론트엔드 직군에서 Javascript로만 테스트를 보는 경우가 많아졌다.</h4>
<p>: 프론트엔드 개발자는 대부분 Javascript로 업무를 한다고 생각하면 된다. 따라서 기업에서도 Javascript가 능숙한 사람을 찾는 것 같다.
(Ex. 배달의 민족, 카카오커머스, 프로그래머스 FE Dev match 등)</p>
<h4 id="2-굳이-다른-언어의-이점을-갖고-갈-필요가-없다">2. 굳이 다른 언어의 이점을 갖고 갈 필요가 없다.</h4>
<p>: Python이 코딩테스트에서 유리한 거 나도 알고 너도 알고 우리 모두가 안다. 그런데 프론트엔드 코딩테스트 문제를 보면 Python이 압도적으로 유리한 알고리즘은 거의 없고, 구현 위주인 경우가 많다고 한다. 따라서 굳이 Python을 선택할 필요가 없다고 보여진다.</p>
<h4 id="3-1번과-대충-비슷한-맥락이지만-실무에서-쓰이는-언어다">3. (1번과 대충 비슷한 맥락이지만) 실무에서 쓰이는 언어다.</h4>
<p>: 코딩테스트에서 가장 많이 쓰이는 언어가 C++이랑 Python이라던데, 프론트엔드 개발자가 실무에서 이 언어들을 얼마나 쓰려나? 아직 주니어 개발자라 모르긴 몰라도 1년에 한 두번 쓸까 말까 할 것 같다. 그러니까 아무리 알고리즘 공부를 열심히 해도, 실무와 따로 노는 느낌이 든다. 내가 Javascript 삽고수면 모르겠는데 아니니까..ㅎㅎ; Javascript로 코딩테스트를 준비하면 실무와 동 떨어진 언어를 공부하면서 오는 현타를 막을 수 있다.</p>
<h3 id="😎-단점도-말-해보자면">😎 단점도 말 해보자면...</h3>
<h4 id="1-nodejs를-완벽-지원하지-않는-알고리즘-플랫폼들">1. Node.js를 완벽 지원하지 않는 알고리즘 플랫폼들</h4>
<p>: Javascript는 Node.js 서버 안에서 실행된다. 백준에서도 Node.js 지원 문제인지 종종 Javascript로는 풀리지 &#39;못&#39;하는 문제가 있다고 한다. 프로그래머스는 잘 지원해준다고 하니, 차라리 코테 공부 초반에는 프로그래머스 정복을 목표로 삼아도 좋을 듯 하다.</p>
<h4 id="2-자료가-없다">2. 자료가 없다.</h4>
<p>: 이게 진짜 크다. 자료가 진짜 없다!</p>
<h4 id="3-느리다">3. 느리다.</h4>
<p>: Javascript는 스크립트 언어여서 C++에 비해 실행이 많이 느리다. 만약 백준에서 시간제한이 빡센 문제를 풀려고 하면 많이 힘들 수도 있다. 그런데 Javascript를 지원하는 기업 테스트에서는 언어적 특성을 고려해 채점하기 때문에 큰 문제는 아닐 것 같다.</p>
<h3 id="😎-그럼에도-불구하고">😎 그럼에도 불구하고...</h3>
<p>장점이 너무 크다고 생각한다. 프론트엔드 개발자에게는 Javascript 숙련도를 올리는 게 제일 중요하다. Javascript로 코딩테스트를 준비하면 업무와 코테를 모두 잡을 수 있었는데 왜 진작 방향을 틀지 않았나 후회도 되지만, 이제라도 내가 정말 원하던 걸 선택하게 돼서 다행이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Base64를 사용해서 이미지를 전달해보자 (2) - 이미지 불러오기 및 선택하기 (Decoding)]]></title>
            <link>https://velog.io/@me-hana/Base64%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A5%BC-%EC%A0%84%EB%8B%AC%ED%95%B4%EB%B3%B4%EC%9E%90-2-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%B6%88%EB%9F%AC%EC%98%A4%EA%B8%B0-%EB%B0%8F-%EC%84%A0%ED%83%9D%ED%95%98%EA%B8%B0-Decoding</link>
            <guid>https://velog.io/@me-hana/Base64%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A5%BC-%EC%A0%84%EB%8B%AC%ED%95%B4%EB%B3%B4%EC%9E%90-2-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%B6%88%EB%9F%AC%EC%98%A4%EA%B8%B0-%EB%B0%8F-%EC%84%A0%ED%83%9D%ED%95%98%EA%B8%B0-Decoding</guid>
            <pubDate>Mon, 11 Jul 2022 01:18:11 GMT</pubDate>
            <description><![CDATA[<h4 id="1-컴포넌트가-전부-마운트-되기-전-이미지들을-db에서-받아오기-위해-useeffect-hook을-사용하고-imagelist에-저장한다">1. 컴포넌트가 전부 마운트 되기 전 이미지들을 DB에서 받아오기 위해 useEffect Hook을 사용하고, imageList에 저장한다.</h4>
<pre><code class="language-javascript">const [imageList, setImageList] = useState();

const getImageApi = async () =&gt; {
  let res = await axios.get(&#39;서버주소&#39;);
  setImageList(res.data);
};

useEffect(()=&gt;{
  getImageApi();
}, []);</code></pre>
<h4 id="2-읽어온-이미지들을-화면에-보여준다">2. 읽어온 이미지들을 화면에 보여준다.</h4>
<blockquote>
<p>서버로 Java Spring을 사용했다고 가정한다. Java에서는 Blob 데이터를 바로 Binary로 응답보내는 것이 아니라, Blob to String 작업을 거친 뒤에 응답을 보낸다. 따라서 클라이언트단에서 <strong>String to Base64</strong> 작업을 해줘야 한다.</p>
</blockquote>
<pre><code class="language-javascript">&lt;div&gt;
  {
    imageList &amp;&amp; imageList.map((item, idx) =&gt; {
      let convertedCode = Buffer.from(item, &quot;base64&quot;);

      return (
        &lt;img
          key={`image-${idx}`}
          style={{ maxWidth: &quot;100%&quot;, height: &quot;auto&quot; }}
          src={`data:image/;base64,${convertedCode}`}
          alt={`image-${idx}`}
        /&gt;
      );
    })
  }
&lt;/div&gt;</code></pre>
<h4 id="3-클릭한-이미지-정보를-저장할-clickedimage를-만들고-이미지를-클릭하면-동작하는-onclickimage-함수를-img-태그에-붙여준다-클릭된-이미지는-컬러로-클릭되지-않은-이미지는-흑백으로-보이도록-img-태그에-조건부-스타일을-추가해준다">3. 클릭한 이미지 정보를 저장할 clickedImage를 만들고, 이미지를 클릭하면 동작하는 onClickImage 함수를 img 태그에 붙여준다. 클릭된 이미지는 컬러로, 클릭되지 않은 이미지는 흑백으로 보이도록 img 태그에 조건부 스타일을 추가해준다.</h4>
<pre><code class="language-javascript">const [clickedImage, setClickedImage] = useState({
  item: &quot;&quot;,
  convertedCode: &quot;&quot;,
});

const onClickImage = (item) =&gt; {
  let convertedCode = Buffer.from(item, &quot;base64&quot;);
  setClickedImage({item: item, convertedCode: convertedCode});
};</code></pre>
<pre><code class="language-javascript">&lt;div&gt;
  {
    imageList &amp;&amp; imageList.map((item, idx) =&gt; {
      return (
        &lt;img
          key={`image-${idx}`}
          style={{
            maxWidth: &quot;100%&quot;,
            height: &quot;auto&quot;,
            filter: clickedImage.item === item ? &quot;grayscale(0%)&quot; : &quot;grayscale(100%)&quot;
          }}
          src={`data:image/;base64,${item}`}
          alt={`image-${idx}`}
          onClick={()=&gt;{
            onClickImage(item);
          }}
        /&gt;
      );
    })
  }
&lt;/div&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Base64를 사용해서 이미지를 전달해보자 (1) - 이미지 저장 (Encoding)]]></title>
            <link>https://velog.io/@me-hana/Base64%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A5%BC-%EC%A0%84%EB%8B%AC%ED%95%B4%EB%B3%B4%EC%9E%90-1-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%A0%80%EC%9E%A5-Encoding</link>
            <guid>https://velog.io/@me-hana/Base64%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A5%BC-%EC%A0%84%EB%8B%AC%ED%95%B4%EB%B3%B4%EC%9E%90-1-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%A0%80%EC%9E%A5-Encoding</guid>
            <pubDate>Fri, 08 Jul 2022 01:32:58 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>input tag에 type을 file로 해주면 파일을 업로드할 수 있고, accept 속성을 사용해서 업로드 될 파일의 타입을 정해줄 수 있다.</p>
<pre><code class="language-javascript">&lt;input name=&quot;sample&quot; type=&quot;file&quot; accept=&quot;image/*&quot; /&gt;</code></pre>
</li>
<li><p>파일을 선택하면 해당 파일을 Base64 인코딩 하고 인코딩 결과를 state에 저장하는 encodeFilteToBase64 함수를 만들어 input 태그에 붙인다.</p>
<pre><code class="language-javascript">const [imageSrc, setImageSrc] = useState(&quot;&quot;);
</code></pre>
</li>
</ol>
<p>const encodeFileToBase64 = (fileBlob: any) =&gt; {
  if (fileBlob) {
    const reader = new FileReader();
    reader.readAsDataURL(fileBlob);
    reader.onload = (event: any) =&gt; {
      setImageSrc(reader.result + &quot;&quot;);
    };
  } else {
    setImageSrc(&quot;&quot;);
  }
};</p>
<pre><code>```javascript
&lt;input name=&quot;sample&quot; type=&quot;file&quot; accept=&quot;image/*&quot;
  onChange={(e: any)=&gt;{encodeFileToBase64(e.target.files[0]);}} /&gt;</code></pre><ol start="3">
<li><p>클릭하면 서버로 requestBody를 전송하는 onClickSave 함수를 만들어 button 태그에 붙인다.</p>
<pre><code class="language-javascript">const onClickSave = async () =&gt; {
 if (imageSrc.length &gt; 0) {
   try {
     let param = { 
       image: imageSrc
         .replace(&quot;data:image/png;base64,&quot;, &quot;&quot;)
         .replace(&quot;data:image/jpeg;base64,&quot;, &quot;&quot;)
         .replace(&quot;data:image/jpg;base64,&quot;, &quot;&quot;),
     };

     let res = await axios.post(&#39;서버주소&#39;, param);
     alert(&quot;전송 성공&quot;);
   } catch(err) {
     alert(&quot;전송 실패&quot;);
     console.err(err);
   }  

 } else {
   alert(&quot;Please select a image.&quot;);
 }
};</code></pre>
<pre><code class="language-javascript">&lt;button onClick={onClickSave}&gt;하하&lt;/button&gt;</code></pre>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[JSX에서 조건부 스타일 설정]]></title>
            <link>https://velog.io/@me-hana/JSX%EC%97%90%EC%84%9C-%EC%A1%B0%EA%B1%B4%EB%B6%80-%EC%8A%A4%ED%83%80%EC%9D%BC-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@me-hana/JSX%EC%97%90%EC%84%9C-%EC%A1%B0%EA%B1%B4%EB%B6%80-%EC%8A%A4%ED%83%80%EC%9D%BC-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Mon, 04 Jul 2022 08:29:43 GMT</pubDate>
            <description><![CDATA[<h3 id="1-각-property에-조건-부여">1. 각 Property에 조건 부여</h3>
<pre><code class="language-javacript">style={{
  width: 조건1 ? true : false,
  backgroundColor: 조건2 ? true : false
}}</code></pre>
<h3 id="2-이벤트와-함께-각-property에-조건-부여">2. 이벤트와 함께 각 Property에 조건 부여</h3>
<pre><code class="language-javacript">style={{
  width: 조건1 ? true : false, 
  backgroundColor: 조건2 ? true : false,
  &quot;&amp;:hover&quot;: {
    backShadow: 조건3 ? true : false
  }
}}</code></pre>
<h3 id="3-properties-객체를-통째로-조건-부여">3. Properties 객체를 통째로 조건 부여</h3>
<pre><code class="language-javacript">style={ 조건 ? {
  width: &quot;80%&quot;,
  backgroundColor: &quot;#FFF&quot;
} : {
  width: &quot;60%&quot;,
  backgroundColor: &quot;#DDD&quot;
}}</code></pre>
<h3 id="4-stylesheetcreate-활용해-배열-형식으로-여러가지-style-class-부여">4. StyleSheet.create 활용해 배열 형식으로 여러가지 Style Class 부여</h3>
<pre><code class="language-javacript">const styles = StyleSheet.create({
  text: {
    height: 40,
    backgroundColor: &quot;#FFF&quot;,
    borderRadius: 5, 
  },
  textvalid: {
    borderWidth: 2,
  },
  textinvalid: {
    borderColor: &#39;red&#39;,
  },
});

&lt;TextInput
    style={[styles.text, touched &amp;&amp; invalid ? styles.textinvalid : styles.textvalid]}&gt;
&lt;/TextInput&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[react-csv를 사용해 axios로 받은 데이터를 CSV형식으로 다운로드 받기]]></title>
            <link>https://velog.io/@me-hana/react-csv%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-axios%EB%A1%9C-%EB%B0%9B%EC%9D%80-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-CSV%ED%98%95%EC%8B%9D%EC%9C%BC%EB%A1%9C-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C-%EB%B0%9B%EA%B8%B0</link>
            <guid>https://velog.io/@me-hana/react-csv%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-axios%EB%A1%9C-%EB%B0%9B%EC%9D%80-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-CSV%ED%98%95%EC%8B%9D%EC%9C%BC%EB%A1%9C-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C-%EB%B0%9B%EA%B8%B0</guid>
            <pubDate>Fri, 29 Apr 2022 05:28:32 GMT</pubDate>
            <description><![CDATA[<h3 id="요청사항">요청사항</h3>
<p>버튼을 누르면 API 요청을 보내고, 응답 데이터로 CSV 파일을 다운로드 할 수 있게 해주세요.
단, 응답 데이터는 한꺼번에 보낼테니 <strong>한 번의 버튼 클릭으로 여러 개의 CSV 파일을 다운로드</strong> 받을 수 있어야 해요.</p>
<h3 id="문제">문제</h3>
<p>CSVLink에 onClick 이벤트를 붙여서 axios로 응답 데이터를 받아오면 되는, 생각할 것도 없는 기능인 줄 알았는데...
응답이 끝나기 전에 CSVLink를 클릭했다고 인식이 되어서 초기 데이터(빈 배열)로 CSV를 다운로드 시켜주더라 ㅎㅎ;</p>
<h3 id="해결">해결</h3>
<ol>
<li>CSVLink에 onClick 이벤트를 붙이지 않고, Button에 onClick을 붙여 API 호출</li>
<li>useEffect를 사용해 응답 데이터가 setState 되는 시점을 감지</li>
<li>useEffect 안에서 useRef를 사용해서 CSVLink를 클릭한 것처럼 동작시킴</li>
<li>한 번의 클릭으로 여러 개의 파일을 다운로드 하게 하려면, useEffect와 useRef를 여러 개 써주면 됨</li>
</ol>
<h3 id="예시-코드">예시 코드</h3>
<pre><code class="language-javascript">import React, { useState, useEffect, useRef, memo } from &#39;react&#39;;
import Button from &#39;@material-ui/core/Button&#39;;
import { CSVLink } from &#39;react-csv&#39;;
import moment from &#39;moment&#39;;
import axios from &#39;axios&#39;;

const CsvSample= memo((props: any) =&gt; {
  const csvLink1: any = useRef();
  const csvLink2: any = useRef();
  const [data1, setData1] = useState&lt;any&gt;([]);
  const [data2, setData2] = useState&lt;any&gt;([]);

  const clickDownload = async () =&gt; {
      // params는 알아서 구성
      let params = {
        param1: &quot;param1&quot;
        param2: &quot;param2&quot;
        param3: &quot;param3&quot;
      };

      await axios
        .post(&#39;url&#39;, params)
        .then((res: any) =&gt; {
          parseData(res);
        })
        .catch((err) =&gt; {
          console.log(err);
        });
  };

  // axios에 대한 response는 알아서 구성 및 react-csv의 data 형식에 맞게 파싱
  const parseData = (input: any) =&gt; {
    let data1_header = input.data.data1.columns;
    let data1_rows = input.data.data1.data;
    let _data1 = [data1_header , ...data1_rows ];

    let data2_header = input.data.data2.columns;
    let data2_rows = input.data.data2.data;
    let _data2 = [data2_header , ...data2_rows ];

    setData1(_data1);
    setData2(_data2);
  };

  useEffect(() =&gt; {
    if (data1 &amp;&amp; csvLink1 &amp;&amp; csvLink1.current &amp;&amp; csvLink1.current.link) {
      setTimeout(() =&gt; {
        csvLink1.current.link.click();
        setData1([]);
      });
    }
  }, [data1]);

  useEffect(() =&gt; {
    if (data2 &amp;&amp; csvLink2 &amp;&amp; csvLink2.current &amp;&amp; csvLink2.current.link) {
      setTimeout(() =&gt; {
        csvLink2.current.link.click();
        setData2([]);
      });
    }
  }, [data2]);

  return (
    &lt;div&gt;
      &lt;Button
        variant=&quot;outlined&quot;
        color=&quot;primary&quot;
        onClick={() =&gt; {clickDownload();}}
        size=&quot;large&quot;
      &gt;
        Download
     &lt;/Button&gt;
     {data1.length &gt; 0 ? (
       &lt;CSVLink
         data={data1}
         ref={csvLink1}
         filename={`csv-1-${moment().format(&#39;YYYYMMDD&#39;)}`}
         style={{ textDecorationLine: &#39;none&#39; }}
       /&gt;
       ) : undefined}
     {data2.length &gt; 0 ? (
       &lt;CSVLink
         data={data2}
         ref={csvLink2}
         filename={`csv-2-${moment().format(&#39;YYYYMMDD&#39;)}`}
         style={{ textDecorationLine: &#39;none&#39; }}
       /&gt;
       ) : undefined}
   &lt;/div&gt;
  );
});

export default CsvSample;</code></pre>
<p>[참고한 사이트]</p>
<ul>
<li><a href="https://velog.io/@khy226/React-csv%EB%A1%9C-csv-%ED%8C%8C%EC%9D%BC-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%ED%95%98%EA%B8%B0">React-csv로 csv 파일 다운로드하기</a></li>
<li><a href="https://github.com/react-csv/react-csv/issues/189">How to work async with react-csv</a></li>
<li><a href="https://encredible.tistory.com/entry/react-csv%EC%97%90%EC%84%9C-onClick-%EC%8B%9C%EC%A0%90%EC%97%90-async%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EB%B0%9B%EC%95%84-%EC%98%A4%EB%8A%94-%EB%B0%A9%EB%B2%95">react-csv에서 onClick 시점에 async로 데이터를 받아 오는 방법</a></li>
</ul>
]]></description>
        </item>
    </channel>
</rss>