<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dev-sjko.log</title>
        <link>https://velog.io/</link>
        <description>차근차근 천천히</description>
        <lastBuildDate>Wed, 01 Mar 2023 09:55:10 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dev-sjko.log</title>
            <url>https://velog.velcdn.com/images/dev-sjko/profile/9a323bf2-16e8-4fba-9623-6dd9f728faad/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dev-sjko.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dev-sjko" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[이벤트 버블링과 useState를 활용한 댓글 수정하기 기능]]></title>
            <link>https://velog.io/@dev-sjko/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B2%84%EB%B8%94%EB%A7%81%EA%B3%BC-useState%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%8C%93%EA%B8%80-%EC%88%98%EC%A0%95%ED%95%98%EA%B8%B0-%EA%B8%B0%EB%8A%A5</link>
            <guid>https://velog.io/@dev-sjko/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B2%84%EB%B8%94%EB%A7%81%EA%B3%BC-useState%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%8C%93%EA%B8%80-%EC%88%98%EC%A0%95%ED%95%98%EA%B8%B0-%EA%B8%B0%EB%8A%A5</guid>
            <pubDate>Wed, 01 Mar 2023 09:55:10 GMT</pubDate>
            <description><![CDATA[<p>리팩토링 기간에 완성하게 된 댓글 수정기능! 
<img src="https://velog.velcdn.com/images/dev-sjko/post/037b9515-3a63-4cb7-b61e-61e40ce21ed8/image.gif" alt=""></p>
<p>일단 다음과 같은 과정을 통해 해당 댓글의 작성자일 때만  <code>수정하기</code> 버튼과 <code>삭제하기</code> 버튼이 보이게 만들었다. </p>
<blockquote>
</blockquote>
<ol>
<li>postId를 통해 해당 게시글에 작성된 댓글을 조회하기 위해 서버에 GET 요청 보냄</li>
<li>이 때 유저의 token을 함께 보냄(Axios instance 활용)</li>
<li>해당 댓글의 작성자인 경우 서버에서 response의 <code>isAuthor</code> 값을 true로 보내줌</li>
</ol>
<p>그 다음 useState 훅을 이용해 현재 댓글을 수정하는 중인지 나타내는 상태값(isEdit)과 그 상태를 바꿔주는 함수를 만들어주었다. </p>
<pre><code class="language-javascript">const [isEdit, setIsEdit] = useState(false);</code></pre>
<p>내가 구현하고 싶었던 모습은 아래와 같았다.</p>
<blockquote>
</blockquote>
<ol>
<li><code>수정하기</code> 버튼을 누르면 <code>isEdit</code> 값이 true로 바뀜</li>
<li>댓글 내용 부분이 댓글을 수정할 수 있는 input으로 변경</li>
<li><code>수정하기</code> 버튼이 <code>수정완료</code> 버튼으로 변경</li>
</ol>
<h2 id="수정하기-클릭-시-input-창으로-변경">수정하기 클릭 시 input 창으로 변경</h2>
<p>댓글 내용 부분을 input으로 변경하는 것은 isEdit 값과 삼항 연산자를 사용하여 구현했다. 이 때 input으로 바뀌었을 때 원래 댓글 내용이 value로 남아있도록 <code>localContent</code>로 넣어줬다! (isEdit이 false일 때는 그냥 원래 댓글 내용만 보이도록 <code>props.content</code>로 설정)</p>
<pre><code class="language-javascript">  const [localContent, setLocalContent] = useState(props.content); 

// ...생략

&lt;CommentContent&gt;
        {isEdit ? (
          &lt;input
            value={localContent}
            onChange={(e) =&gt; setLocalContent(e.target.value)}
          /&gt;
        ) : (
          props.content
        )}
&lt;/CommentContent&gt;</code></pre>
<h2 id="수정하기-클릭-시-버튼-글자-수정완료로-변경">수정하기 클릭 시 버튼 글자 수정완료로 변경</h2>
<p>여기에서 isEdit 값이 true 일 때만 <code>수정완료</code> 버튼으로 바뀌도록 하는 과정이 가장 고민이었다. isAuthor 값이 true 일 때만 <code>수정하기</code> 버튼과 <code>삭제하기</code> 버튼이 렌더링 되도록 이미 삼항연산자로 만들어 준 상태였는데 &#39;그럼 삼항 연산자를 중첩해야하나&#39; 고민하던 중, 정말 번뜩 스터디에서 공부했던 이벤트 버블링 개념이 떠올랐다. </p>
<p>✏️ <strong>이벤트 버블링</strong> : 하위 요소의 이벤트가 상위 요소의 이벤트 핸들러에 영향을 주는 현상</p>
<pre><code class="language-javascript">&lt;CommentModifyButton onClick={() =&gt; setIsEdit(!isEdit)}&gt;
            {isEdit ? (
              &lt;span onClick={() =&gt; commentPatchMutation.mutate()}&gt;
                수정완료
              &lt;/span&gt;
            ) : (
              &lt;span&gt;수정하기&lt;/span&gt;
            )}
&lt;/CommentModifyButton&gt;</code></pre>
<p>그래서 위와 같이 <code>isEdit</code> 값을 토글시키는 setIsEdit은 <code>CommentModifyButton</code> 태그에 등록해주고, isEdit 값이 true 일 때만 렌더링되는 <code>수정완료</code> span 태그에 PATCH 요청을 보내는 mutation 함수를 등록했다. </p>
<p>이렇게 하면 isEdit 값이 true 일 때는 <code>수정완료</code>가 렌더링 되고 그 <code>수정완료</code>를 클릭하면 부모 요소인 <code>CommentModifyButton</code>에도 클릭 이벤트가 전파되기 때문에 자동으로 setIsEdit 함수가 실행된다! 그럼 isEdit 값이 다시 false가 되면서 <code>수정하기</code> 버튼으로 변경되고 input으로 되어있던 UI도 수정된 내용으로 바뀐다. </p>
<h2 id="완성된-기능">완성된 기능</h2>
<pre><code class="language-javascript">      {props.isAuthor &amp;&amp; (
        &lt;div className=&quot;commentButton&quot;&gt;
          &lt;CommentModifyButton onClick={() =&gt; setIsEdit(!isEdit)}&gt;
            {isEdit ? (
              &lt;span onClick={() =&gt; commentPatchMutation.mutate()}&gt;
                수정완료
              &lt;/span&gt;
            ) : (
              &lt;span&gt;수정하기&lt;/span&gt;
            )}
          &lt;/CommentModifyButton&gt;
          &lt;CommentModifyButton onClick={() =&gt; commentDeleteMutation.mutate()}&gt;
            삭제하기
          &lt;/CommentModifyButton&gt;
        &lt;/div&gt;
      )}</code></pre>
<p><img src="https://velog.velcdn.com/images/dev-sjko/post/fff1136b-9442-415d-aab0-f57e2d7b6c2b/image.gif" alt=""></p>
<hr>
<p>자바스크립트의 이벤트 플로우는 비교적 최근에 알게 된 개념인데 이렇게 스터디에서 이론적으로 공부한 내용을 기능 구현에 직접 적용해보니까 기억에도 훨씬 더 잘 남는다. 고민 많았던 기능 구현에 성공한 기쁨은 덤🥰✨</p>
<p>독서기록 프로젝트에서 워낙 새롭게 알게된 것들이 많아서 임시저장글은 쌓여가고...😂 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React와 Node.js에서 네이버 책 검색 API 활용하기(feat.axios)]]></title>
            <link>https://velog.io/@dev-sjko/react-node-js-naver-book-search-api</link>
            <guid>https://velog.io/@dev-sjko/react-node-js-naver-book-search-api</guid>
            <pubDate>Sat, 28 Jan 2023 09:05:55 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dev-sjko/post/2d88ec29-31af-4b1e-8c18-e479538f58b2/image.png" alt="">
독서 기록 서비스(Book극곰)를 만들 때 제일 구현 하고싶었던 기능인 책 검색 API! 이를 만들면서 겪은 시행착오와 어떻게 구현했는지 기록✨</p>
<blockquote>
<p>네이버 검색 API를 이용하려면 <a href="https://developers.naver.com/main/">네이버 개발자 센터</a>에서 먼저 내 애플리케이션을 등록해야한다.
<a href="https://developers.naver.com/docs/serviceapi/search/book/book.md#%EC%B1%85">네이버 책 검색 API 설명서</a></p>
</blockquote>
<p>카카오와 알라딘에서도 도서 검색 OPEN API를 제공하지만 나는 기존에 등록했던 애플리케이션이 있었기 때문에 네이버 책 검색 API를 사용하기로 했다. 
<img src="https://velog.velcdn.com/images/dev-sjko/post/b5b06887-f9ad-4e56-8bf8-997cd8468984/image.png" alt="">
설명서의 방법대로 요청 url과 header를 세팅하고 포스트맨에서 성공적으로 응답을 받은 것 까지는 좋았는데..
<img src="https://velog.velcdn.com/images/dev-sjko/post/47f6e943-e0de-4706-9b77-3458abf482d8/image.png" alt="">
브라우저에서 맞닥뜨린 CORS 에러😭(예상은 했지만..)</p>
<p>그래서 <a href="https://cors.sh/">https://cors.sh/</a> 라는 proxy를 거쳐서 요청을 보내니까 이번에는 _request header 혹은 cookie가 너무 크다_는 에러가 떴다. </p>
<p>쿠키를 지워보고, 헤더 설정을 바꿔보고 해봐도 해결은 안되고 시간이 너무 오래 걸려서 카카오나 다른 도서 검색 API를 사용해야 하나, 다른 proxy middleware를 설치해야하나 고민하다가 &#39;어쨌든 포스트맨에서는 문제없이 동작하니까 백엔드 쪽에서 <code>imageRouter</code>를 만들어서 요청을 보내는 코드를 짜보면 어떨까!🤨&#39; 하는 (무모한) 생각이 들었고 한 번 해보자 싶어서 back 디렉토리에서 작업을 했다.</p>
<blockquote>
<p><a href="https://developers.naver.com/docs/serviceapi/search/blog/blog.md#node-js">네이버 검색 API 예제</a>에서 Node.js 코드는 request 객체를 이용하는 걸로 나와있었는데 나는 axios로 요청을 보냈다. (var 키워드도 고치고..)</p>
</blockquote>
<pre><code class="language-javascript">// imageRouter.js
import { Router } from &quot;express&quot;;
import axios from &quot;axios&quot;;
import { config } from &quot;../config&quot;;

export const imageRouter = Router();

function imageSearch(req, res, next) {
  const api_url =
    &quot;https://openapi.naver.com/v1/search/book.json?query=&quot; +
    encodeURI(req.query.query);
  axios
    .get(api_url, {
      headers: {
        &quot;X-Naver-Client-Id&quot;: config.naverBook.clientID,
        &quot;X-Naver-Client-Secret&quot;: config.naverBook.clientSecret,
      },
    })
    .then((data) =&gt; {
      res.send(data.data.items);
    })
    .catch((err) =&gt; next(err));
}

imageRouter.get(&quot;/&quot;, imageSearch);</code></pre>
<p>그리고 config 파일에서 <code>clientID</code>와 <code>clientSecret</code>을 설정해주고, app 파일에서 <code>imageRouter</code>를 등록해주면 백에서는 작업 끝!</p>
<hr>
<p>그리고 프론트에서는</p>
<blockquote>
</blockquote>
<ol>
<li><code>query</code> 값으로 사용자가 검색창에 입력한 검색어를 넣어주고 imageRouter에 해당하는 url로 GET 요청</li>
<li>해당 검색어로 검색된 결과를 화면에 렌더</li>
<li>내가 원하는 책을 클릭하면 해당 item이 갖고있는 image url을 state에 저장</li>
<li>독서 기록 POST 요청을 보낼 때 title(제목), content(내용)과 함께 url 값을 서버에 전달</li>
</ol>
<p>위와 같은 방법으로 책 이미지 검색 기능을 구현했다!</p>
<pre><code class="language-javascript">// imageSearchModal.tsx
type ImageSearchModalProps = {
  setBookImageUrl: Dispatch&lt;SetStateAction&lt;string&gt;&gt;;
  setModalState: Dispatch&lt;SetStateAction&lt;boolean&gt;&gt;;
};

type BookInfo = { [key: string]: string };

const ImageSearchModal = (props: ImageSearchModalProps) =&gt; {
  const [bookSearchKeyword, setbookSearchKeyword] = useState(&quot;&quot;);
  const [bookSearchResult, setbookSearchResult] = useState(Array&lt;BookInfo&gt;);
  const handleImageSearchInputChange = (
    e: React.ChangeEvent&lt;HTMLInputElement&gt;,
  ) =&gt; {
    setbookSearchKeyword(e.target.value);
  };

  const handleImageSearchClick = async () =&gt; {
    try {
      if (bookSearchKeyword === &quot;&quot;) {
        console.log(&quot;검색어 없음&quot;);
        return;
      }
      const { data } = await axios.get(`/api/image?query=${bookSearchKeyword}`);
      setbookSearchResult([...data]);
    } catch (err) {
      console.log(err);
    }
  };

  return (
    &lt;Modal title=&quot;이미지 검색하기&quot; setModalState={props.setModalState}&gt;
      &lt;ImgSearchModalWrap&gt;
        &lt;ImgSearchBar&gt;
          &lt;ImgSearchInput
            name=&quot;img&quot;
            id=&quot;img&quot;
            placeholder=&quot;책 제목, 지은이, 키워드로 검색할 수 있습니다.&quot;
            value={bookSearchKeyword}
            onChange={handleImageSearchInputChange}
          /&gt;
          &lt;MyButton btntype=&quot;basic&quot; onClick={handleImageSearchClick}&gt;
            검색
          &lt;/MyButton&gt;
        &lt;/ImgSearchBar&gt;
        {bookSearchResult.map((item, index) =&gt; (
          &lt;ImageSearchResult
            key={index}
            item={item}
            setBookImageUrl={props.setBookImageUrl}
            setModalState={props.setModalState}
          /&gt;
        ))}
      &lt;/ImgSearchModalWrap&gt;
    &lt;/Modal&gt;
  );
};

export default ImageSearchModal;</code></pre>
<pre><code class="language-javascript">// imageSearchResult.tsx
type BookItem = {
  item: { [key: string]: string };
  setBookImageUrl: Dispatch&lt;SetStateAction&lt;string&gt;&gt;;
  setModalState: Dispatch&lt;SetStateAction&lt;boolean&gt;&gt;;
};

const ImageSearchResult = ({
  item,
  setBookImageUrl,
  setModalState,
}: BookItem) =&gt; {
  const handleSearchResultItemClick = () =&gt; {
    setBookImageUrl(item.image);
    setModalState(false);
  };
  return (
    &lt;SearchResultItem onClick={handleSearchResultItemClick}&gt;
      &lt;div&gt;
        &lt;SearchResultBookImg src={item.image} /&gt;
      &lt;/div&gt;
      &lt;SearchResultItemDetail&gt;
        &lt;SearchResultItemTitle&gt;
          {item.author},{item.title}
        &lt;/SearchResultItemTitle&gt;
        &lt;SearchResultItemDescription&gt;
          {item.description}
        &lt;/SearchResultItemDescription&gt;
      &lt;/SearchResultItemDetail&gt;
    &lt;/SearchResultItem&gt;
  );
};

export default ImageSearchResult;</code></pre>
<p>그래서 완성된 결과!
<img src="https://velog.velcdn.com/images/dev-sjko/post/4594e16d-57b8-49dc-a7da-2fb084703166/image.gif" alt=""></p>
<hr>
<p>의문이 남는 점 + 아쉬운 점을 적어보자면,</p>
<ul>
<li>검색 모달창 상태를 바꾸는 <code>setIsImageSearchModalOpen</code> 함수와 이미지 url 상태값 받아서 바꾸는 <code>setBookImageUrl</code> 함수를 NewContent 컴포넌트에서 ImageSearchModal 컴포넌트를 거쳐 ImageSearchResult 컴포넌트까지 prop으로 내려주고 있는데 이것도 props drilling이라고 할 수 있는지?</li>
<li>다른 백엔드 router에서는 util에 있는 asyncHandler를 사용해서 요청과 에러처리를 해주는데 나는 그냥 then과 catch만 사용해서.. 이건 사용자가 검색어에 빈 값을 입력했을 때는 어떻게 보여줘야 할지 생각하다가 발견한 문제라서 다른 코드들을 참고해서 에러 처리를 수정해줘야 할 것 같다. </li>
</ul>
<p>여담으로는, 아주 예전에 네이버 영화 검색 기능을 구현하다가 너무 어려워서 결국 포기했던 기억이 있는데 이번에 이렇게 책 api를 사용해서 요청을 보내고 응답을 받고 그걸 활용해서 기능을 구현할 수 있었다는 거 자체가 너무 감격이었다..😭 다음에는 책 말고 뉴스나 지역 관련된 api도 꼭 활용해보고 싶다!💪</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS S3, multer를 이용해 DB에 이미지 저장하기(feat. formData)]]></title>
            <link>https://velog.io/@dev-sjko/AWS-S3-multer-formData</link>
            <guid>https://velog.io/@dev-sjko/AWS-S3-multer-formData</guid>
            <pubDate>Thu, 19 Jan 2023 09:45:34 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dev-sjko/post/5b27a689-7757-40c8-a20d-90e33c384e69/image.gif" alt="">
미술 작품 거래 서비스(Decouvrir) 프로젝트를 할 때, 유저가 자신의 작품을 판매하기 위해 직접 상품을 등록할 수 있는 기능이 필요했다. 그 기능의 포인트는 이미지를 첨부파일로 받아서 DB에 상품 이름, 가격 등과 함께 저장하는 로직이었는데, 이를 위해서</p>
<blockquote>
</blockquote>
<ol>
<li>AWS S3에 이미지를 업로드</li>
<li>이미지 url을 클라이언트로 일단 응답</li>
<li>클라이언트에서는 그 경로와 함께 다른 텍스트 정보들을 서버에 전달</li>
<li>서버에서 상품 DB에 정보를 저장</li>
</ol>
<p>위와 같은 방법으로 최종 정리되었다. </p>
<hr>
<p>여러 서칭을 통해 AWS S3 버킷 세팅을 마쳤고 multer-s3를 이용해 파일을 업로드 하기 위한 imageRouter의 코드는 아래와 같이 작성했다. </p>
<pre><code class="language-javascript">import dotenv from &quot;dotenv&quot;;
import AWS from &quot;aws-sdk&quot;;
import multerS3 from &quot;multer-s3&quot;;
import multer from &quot;multer&quot;;
import { Router } from &quot;express&quot;;

// loads .env file contents into process.env
dotenv.config();

const imageRouter = Router();

// aws s3 설정
AWS.config.update({
  region: process.env.AWS_REGION,
  apiVersion: &quot;latest&quot;,
  credentials: {
    accessKeyId: process.env.S3_ACCESS_KEY_ID,
    secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
  },
});

const s3 = new AWS.S3();

// multer를 이용해 이미지를 업로드하는 함수
const imageUpload = multer({
  storage: multerS3({
    //저장 공간 정보
    s3: s3,
    bucket: &quot;decouvrir&quot;,
    key: function (req, file, cb) {
      // 확장자가 올바른지 확인
      const extention = file.mimetype.split(&quot;/&quot;)[1];
      if (![&quot;png&quot;, &quot;jpg&quot;, &quot;jpeg&quot;, &quot;gif&quot;, &quot;bmp&quot;].includes(extention)) {
        return cb(new Error(&quot;이미지 파일을 등록해주세요.&quot;));
      }
      // 파일 이름 지정
      cb(null, `${Date.now()}_${file.originalname}`);
    },
  }),
  acl: &quot;public-read&quot;,
});

// post 요청이 들어왔을 때
imageRouter.post(
  &quot;/upload&quot;,
  // 하나의 이미지 파일 업로드
  imageUpload.single(&quot;image&quot;),
  async (req, res, next) =&gt; {
    try {
      res.json({
        // 이미지 경로를 res로 보내줌
        message: &quot;이미지 저장 성공&quot;,
        imagePath: req.file.location,
      });
    } catch (err) {
      next(err);
    }
  }
);

export { imageRouter, imageUpload };</code></pre>
<p>이렇게 작성을 하고 postman을 통한 테스트는 완료되었는데, 프론트에서 API 요청을 보내면 계속 오류가 나는 것이다ㅠㅠ 함참동안 뭐가 잘못되었을까 고민했는데, 결국 문제점을 찾아냈다. </p>
<pre><code class="language-javascript">function addProduct(e) {
  e.preventDefault();
  const productName = productNameInput.value;
  const content = contentInput.value;
  const price = priceInput.value;
  const category = categoryInput.value;

  const formData = new FormData();
  formData.append(&quot;image&quot;, photoFile.files[0]); // 파일 첨부

  fetch(`/api/images/upload`, {
    method: &quot;POST&quot;,
    body: JSON.stringify(formData), 
  })
    .then((response) =&gt; response.json())
    .then((data) =&gt; {
      image = data.imagePath;
      const newProductData = {
        painterEmail,
        painterName,
        productName,
        price,
        content,
        category,
        categoryId,
        image,
      };

      Api.post(`/api/product`, newProductData);
      alert(&quot;상품 등록이 완료되었습니다!&quot;);
    });

}

postBtn.addEventListener(&quot;click&quot;, addProduct);</code></pre>
<p>imageRouter로 POST 요청을 보낼 때 <code>body</code>를 그냥 <code>formData</code>로 했어야 했는데 <code>JSON.stringify(formData)</code>로 보내고 있었던 것이 원인이었다😭 내가 생성해준 formData 객체를 그냥 보내줬으면 됐는데 그걸 stringify 했으니 제대로 업로드되지 않았던 것.. 형식이 다른 데이터를 어떻게 보내야 하는지에 대한 고민이 부족했던 것이 느껴져서 많이 반성했다. (이렇게 간단한 걸 며칠동안 붙잡고 있었다니..)</p>
<hr>
<p>더 알아보니 요청 헤더에 담기는 정보인 Content-type 중 <code>multipart</code> 자체가 폼에 이미지와 같은 <strong>파일</strong>과 제목, 설명과 같은 <strong>텍스트</strong>처럼 서로 다른 형식의 데이터가 함께 서버로 전달되어야 할 때 사용할 수 있는 타입이기 때문에 내가 했던 것 처럼 api 요청을 두 번 보낼 필요 없이 formData에 모든 정보를 담아서 한 번에 전달할 수 있다고 한다. 일단 S3에 이미지를 올리는 건 생각하지 말고, formData를 활용하는 방법을 더 익혀야겠다고 생각했다. (이미지가 다뤄지지 않는 웹서비스는 드물기 때문에 더더욱!💪)</p>
<blockquote>
<p>참고 자료</p>
</blockquote>
<ul>
<li><a href="https://inpa.tistory.com/entry/AWS-SDK-%F0%9F%91%A8%F0%9F%8F%BB%E2%80%8D%F0%9F%92%BB-Multer-S3-%EC%97%B0%EB%8F%99-%EB%B0%8F-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%A0%95%EB%A6%AC">multer-s3 관련 포스팅</a></li>
<li><a href="https://velog.io/@shin6403/HTTP-multipartform-data-%EB%9E%80">multipart/form-data 관련 포스팅</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[엘리스 SW 엔지니어 트랙 3기] 최종 회고]]></title>
            <link>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0-%EC%B5%9C%EC%A2%85-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0-%EC%B5%9C%EC%A2%85-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 02 Jan 2023 15:52:11 GMT</pubDate>
            <description><![CDATA[<p>마지막 회고를 쓰려고 하니 너무 많은 생각이 스쳐지나간다😭</p>
<hr>
<h3 id="엘리스를-만나기-전의-나">엘리스를 만나기 전의 나,</h3>
<p>2022년 1월, 퇴사를 한 나는 기로에 서있었다. 외고, 대학, 첫 취업까지 약 10년 동안 준비하고 걸어왔던 국제회의 기획사로서의 길을 계속 갈 것인지, 아니면 내가 잘할 수 있을지 없을지도 모르지만 정말 도전해보고 싶었던 개발 공부를 시작해볼 것인지. 사실 한 번 하고싶다고 마음 먹으면 발을 조금이라도 담가보기는 해야 하는 성격으로 인해 빠르게 개발 공부를 선택할 수 있었다. </p>
<p>작년 중순까지는 막연히 프론트엔드에 관심을 두면서 독학을 해나갔는데, 혼자 공부하는 시간이 길어지자 문득 내가 이걸 잘 하고 있는 건지, 실전에서는 어느정도의 실력을 필요로 하는지 의문이 생겼다. 그리고 이 질문들에 답해줄 수 있는 사람이 주위에 없다는 것에 따른 막막함과 답답함이 함께 찾아왔고, 그때부터 국비 교육을 찾아보기 시작해서 최종적으로는 엘리스 SW 엔지니어 트랙 3기에 합격!</p>
<blockquote>
<p><a href="https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0">엘리스 SW 엔지니어 트랙 3기 지원 &amp; 합격 후기</a></p>
</blockquote>
<hr>
<h3 id="꼭-지켰던-한-가지-규칙">꼭 지켰던 한 가지 규칙</h3>
<p>수업을 듣기 시작하고 제일 처음 든 생각은 ‘4개월이 미친듯이 빨리 가겠구나’였다. </p>
<p>매일 이렇게 쫓기듯 공부하다보면 나중에는 분명 내가 뭘 했는지 기억도 나지 않을 것이라는 불길한 확신에 첫 실시간 수업 때부터 그 날은 무엇에 대해서 공부했는지, 그리고 공부를 하면서 든 생각들을 매일매일 기록해나갔다. </p>
<p>과거에 남겨놓은 기록이 얼마나 큰 힘으로 돌아오는지 알기 때문에 이것만큼은 절대 놓치지 않고 꼭 하려고 노력했고 (너무 힘들 때는 감정 쓰레기통이라고 생각하면서 쓰기도 했지만😅) 시간이 지날수록 ‘이 때는 아무것도 몰라서 진짜 힘들었는데 그래도 지금은 조금이나마 알고있네?’라고 생각하는 순간이 점점 많아졌다.</p>
<p><img src="https://velog.velcdn.com/images/dev-sjko/post/4cdefd3a-9e78-42b9-bc90-367c1f05efa4/image.gif" alt=""></p>
<hr>
<h3 id="엘리스에서-깨달은-사실">엘리스에서 깨달은 사실</h3>
<p>나는 정말 팀프로젝트에 최적화 된 인간이었다..</p>
<p>그냥 팀에 묶이지 않고 혼자 뭔가를 하는 것과는 차원이 다른 성과가 나오는 걸 보고 그만큼 내가 팀에 느끼는 책임감과, 팀원들에게서 받는 에너지가 엄청나게 크다는 것을 새롭게 알았다. </p>
<p>처음 엘리스 트랙에 지원했을 때도 팀프로젝트와 스터디 활동에 기대가 많았고, 두 번의 스터디와 프로젝트 기간동안 내 인생에서 이만큼 뭔가에 열정을 쏟은 적이 있었나 싶을 정도로 최선을 다했었지만 마지막 수료식에서 받은 리더십상으로 그 노력을 확인받은 것 같아서 더더욱 뿌듯했다😁</p>
<p><img src="https://velog.velcdn.com/images/dev-sjko/post/ea81101f-45a1-47b9-8d56-252e898aa384/image.jpg" alt=""></p>
<hr>
<h3 id="엘리스에-있는-동안">엘리스에 있는 동안,</h3>
<p>좋은 사람들을 정말 많이 만났다! </p>
<p>비전곰 스터디원들은 엘리스 내내 존재만으로도 힘이 되는 사람들이었고 1차 프로젝트, 2차 프로젝트 하면서 정말 많이 친해진 우리 플젝 팀원들도 너무 소중한 인연들ㅠㅠ💕 </p>
<p>또 모르는 게 있을 때 물어보면 바로 답해주던 상냥한 레이서들, 얘기는 많이 나누지 못했어도 낙낙에서 항상 만나던 다른 레이서들, 그리고 코치님들과 매니저님들까지! </p>
<p>이렇게 다양한 사람들과 함께하며 배우고 공부할 수 있었다는 것이 내게는 더없이 커다란 행운이었다. 
<img src="https://velog.velcdn.com/images/dev-sjko/post/3f943645-8b19-4dc5-b53a-42d33c620949/image.png" alt=""></p>
<hr>
<h3 id="엘리스를-마치며">엘리스를 마치며</h3>
<p>처음 개발 공부를 시작했을 때와는 많이 달라진 나 자신의 모습을 보면서 이제는 개발자로 일하는 나의 미래를 구체적으로 상상할 수 있게 되었다. 끝이나 이별에 취약한 인간이지만, 앞으로의 여정을 함께 할 동료들이 있다는 것이 너무나 든든하고, 지금은 걱정이나 불안보다 앞으로가 기대되는 마음이 더 크다!💜 현재의  목표는 개인으로서든, 팀으로서든 얼른 새로운 프로젝트를 시작하는 것ㅎㅎㅎ </p>
<p>엘리스에서 보낸 4개월의 희로애락을 기억하면서 이제 취업길..을 걸어야지..! </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[엘리스 SW 엔지니어 트랙 3기] 13~14주차 회고]]></title>
            <link>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0-1314%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0-1314%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 18 Dec 2022 18:13:42 GMT</pubDate>
            <description><![CDATA[<hr>
<p><img src="https://velog.velcdn.com/images/dev-sjko/post/8b013484-faf9-4ead-81a3-86ccab2e037f/image.jpg" alt="">
낙낙 라운지에도 찾아온 크리스마스🎄</p>
<hr>
<h2 id="2차-프로젝트">2차 프로젝트</h2>
<p>엘리스 SW 엔지니어 트랙 3기의 2차 프로젝트(이자 마지막 프로젝트)가 시작되었다! 이번에는 1차 프로젝트 때 프론트엔드와 백엔드를 둘 다 겪어본 경험을 바탕으로 팀장이 되었다ㅎㅎ </p>
<p>시작 단계에서 프로젝트 세팅할 때 eslint나 prettier도 거의 처음 다뤄보고 프론트에서 사용할  tailwind와 styled-components가 결합된 tailwind-styled-components 라이브러리도 제대로 작동하는지 확인하고 올리느라 세팅이 너무 오래 걸려서 팀원들한테도 미안한 마음이었는데.. 이것 말고도 여러가지로 인해 사흘 정도는 팀 안팎으로 좀 우당탕탕 했던 것 같다.</p>
<p>본격적으로 작업을 시작한 지금은 나도 내 역할을 잘 해내야겠지만 팀원들이 많이 얻어갔으면 좋겠는 마음이 크다. 하고 싶다고 말한 것들도 다 해보고, 아쉬움 없이! 마지막이니까! </p>
<p>처음에는 의욕에 불타서 &quot;지금까지 배운 것들 다 써야지!&quot; 라고 생각했는데 마음처럼, 생각처럼 되지 않는 부분도 분명히 있을테니 속도를 낼 때는 내더라도 무리한 욕심 때문에 멘탈 망가지는 일은 없길..!(사실 이미 너덜너덜인 멘탈</p>
<hr>
<p>이제 프로젝트만 끝나면 엘리스 트랙도 끝이라 이력서와 포폴 준비에 대한 부담이 슬슬 몰려오는데.. (부담 느낀지는 좀 됐지만^^) 잘 할 수 있을까😭</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[엘리스 SW 엔지니어 트랙 3기] 11~12주차 회고]]></title>
            <link>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0-1112%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0-1112%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Tue, 06 Dec 2022 12:16:21 GMT</pubDate>
            <description><![CDATA[<p>비전곰🐻들의 독서 기록 웹서비스 <strong>Book극곰</strong>🐻‍❄️</p>
<hr>
<p>1차 프로젝트가 끝나고 리액트를 공부하는 기간에 1차 스터디부터 함께해온 팀원들과 2차 스터디로 리액트 토이 프로젝트를 진행했다. 리액트가 낯선 팀원들이 많아서 첫 주에는 수업을 따라가면서 기본 개념을 먼저 익히고 11주차부터 본격적으로 프로젝트에 들어갔다. 개발자로 진로를 바꾸면서 만들어보고 싶다고 생각만 했던 독서 기록 웹서비스로 주제가 정해진 것도 기쁜데, 그걸 또 좋아하는 동료들과 함께 만들 수 있다니 이건.. 재미없을 수가 없지...😆</p>
<p>나는 <strong>새 독서 기록 생성 페이지</strong>와 <strong>독서 기록 상세 페이지</strong>, 그리고 <strong>댓글</strong> 기능을 담당했는데 일단 가장 어려웠던 문제는 _&#39;로그인 한 유저가 해당 글의 작성자임을 어떻게 판단할 것인가?&#39;_였다. 자신이 쓴 작성글에서만 <code>수정하기</code>와 <code>삭제하기</code> 버튼이 보이도록 하고 싶어서 상세 페이지에 들어갔을 때 현재 로그인 한 유저의 정보와 해당 게시글의 작성자 정보를 비교하는 로직을 넣어서 구현하려고 했는데 그러다보니 state가 많아지기도 하고 뭔가 더 좋은 방법이 있을 것 같은데 싶었던 기억이 남아있다. (무한 렌더링의 늪에서 헤어나오는 데에 이틀 걸린 건 안비밀..) 이번 프로젝트를 계기로 Redux를 얼른 배워야겠다고 다짐했(지만 너무 어렵)다..! </p>
<p>물론 &#39;나는 왜 이렇게밖에 못할까&#39;, &#39;진작 열심히 좀 할 걸&#39; 후회하는 자책 시간도 빼놓을 수 없었지만(^^) 어려울 때 물어보면 언제나처럼 달려와서 같이 찾아봐주고 고민해주는 팀원들 덕분에 이번에 정말 짧은 기간 안에 생각보다 더 완성도 높은 페이지를 만들 수 있었다! 이번 발표도 내가 맡았는데 못해도 된다고, 떨지 말라고 응원 해주고 발표 마치고 모여서 이야기한 것들도 정말 평생 못잊을 것 같다. 진짜 제 5년치 행운 이 팀원들 만나려고 다 썼어요 엉엉..😭</p>
<p>지금도 정말 많은 고민을 쏟은 프로젝트지만 더 퀄리티를 높이기 위해서 남은 기간동안 또 열심히 배워보기로! </p>
<blockquote>
</blockquote>
<p><strong>✏️ 구현한 기능</strong></p>
<ul>
<li>useState를 활용해서 작성자에게만 수정, 삭제 버튼 보이도록 구현</li>
<li>axios 사용해서 DB에 있는 독서 기록 불러오고 렌더링하기</li>
<li>새로운 독서 기록 생성하기</li>
<li>새로운 댓글 생성하기<blockquote>
</blockquote>
</li>
<li><em>✏️ 구현할 기능*</em></li>
<li>게시글 수정, 삭제</li>
<li>댓글 작성자가 자신의 댓글 수정, 삭제</li>
<li>책 API를 사용한 이미지 검색</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[엘리스 SW 엔지니어 트랙 3기] 9~10주차 회고]]></title>
            <link>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0-910%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0-910%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sat, 19 Nov 2022 14:38:25 GMT</pubDate>
            <description><![CDATA[<hr>
<p><img src="https://velog.velcdn.com/images/dev-sjko/post/00ff1713-d545-4f13-b5ab-6b642d082d03/image.gif" alt="">
멈출 수 없는 토끼🐰팀의 <strong>Découvrir</strong></p>
<hr>
<h3 id="1차-프로젝트-그-후">1차 프로젝트 그 후...</h3>
<p>1차 프로젝트가 벌써 끝났다니😢 기억에 남는 일도 많고 팀원 모두가 정말 고생을 많이 한 첫 팀프로젝트였다. 중간에 이슈가 생겨서 내가 프론트엔드에서 백엔드 포지션으로 옮기게 되었는데, 팀에 도움이 될 수 있을까 하는 걱정이 들 때마다 옆에서 할 수 있다고 격려해주고 뭐든 문제가 생기면 함께 해결해나갈 수 있는 팀원들이 있다는 사실을 되뇌이면서 달려나간 2주였다.<br>물론 이론에서 배운 것들을 직접 사용해보면서 더 공부하고 배울 수 있었던 점도 좋았지만 함께했던 팀원들이 없었다면 절대 완성할 수 없었던 프로젝트였기에.. 팀원들의 에너지를 받아서 더 가파르게 성장할 수 있었던 시간이었다고 생각한다!ㅎㅎ 프로젝트는 끝났지만 아직 부족한 점도, 구현하고 싶은 기능도 많이 남아있어서 앞으로도 팀원들과 시간이 될 때마다 서비스를 더 발전시켜 나가보고 싶다!🐰</p>
<h3 id="잘한-점--개선할-점">잘한 점 &amp; 개선할 점</h3>
<p>1차 프로젝트 무사히 마무리 한 건 정말 잘했다!👏 플젝 끝났다고 바로 주말에 냅다 자버린 건 안 잘했다..!😔</p>
<h3 id="목표">목표</h3>
<p>1차 플젝 후기를 길게 썼지만 사실 지금 수업 진도는 리액트를 바쁘게 달리고 있다..ㅎ 이번에 프로젝트로 만난 팀원과 앞으로도 성수낙낙에 자주 가서 공부 하기로 해서(집에서 공부 못하는 새럼들😞) 무사히 낙낙 출첵하기를 목표로..!✅</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React에서 리스트 사용하기]]></title>
            <link>https://velog.io/@dev-sjko/React%EC%97%90%EC%84%9C-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dev-sjko/React%EC%97%90%EC%84%9C-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 16 Nov 2022 07:48:49 GMT</pubDate>
            <description><![CDATA[<h3 id="📌-우리가-작성한-일기를-배열에-저장하고-리스트를-렌더링-해보자">📌 우리가 작성한 일기를 배열에 저장하고 리스트를 렌더링 해보자</h3>
<p>*React에서 배열은 게시글, 피드, 리스트를 다루는 데에 많이 사용됨</p>
<hr>
<p>✏️ <strong>일기 리스트 컴포넌트 만들기</strong>
파일 : DiaryList.js</p>
<pre><code class="language-javascript">const DiaryList = () =&gt; {
    return &lt;div className=&quot;DiaryList&quot;&gt;
        &lt;h2&gt;일기 리스트&lt;/h2&gt;
    &lt;/div&gt;
}

export default DiaryList;</code></pre>
<ul>
<li>컴포넌트 추가 후에 App.js에서 import 해오는 거 잊지 말기!</li>
</ul>
<hr>
<p>✏️** 임시 배열로 리스트 렌더링 하는 것 먼저 연습하기**
목표 : 일기 목록 배열을 DiaryList 컴포넌트에 prop으로 전달해서 그 데이터를 렌더링</p>
<ul>
<li>일기 dummyList 만들어주기<pre><code class="language-javascript">// App.js
import &#39;./App.css&#39;;
import DiaryEditor from &#39;./DiaryEditor&#39;;
import DiaryList from &#39;./DiaryList&#39;;
</code></pre>
</li>
</ul>
<p>const dummyList = [
{
  id:1,
  author:&quot;hailee&quot;,
  content:&quot;hi&quot;,
  emotion:5,
  created_date: new Date().getTime(), // 현재 시간을 ms로 반환
},
... // 똑같은 구조의 일기 객체 3개 만들어줌
]</p>
<p>function App() {
  return (</p>
<div className="App">
  <DiaryEditor/>
  <DiaryList diaryList={dummyList}/>
</div>
  );
}

<p>export default App</p>
<pre><code>- DiaryList 컴포넌트에서 prop으로 받아주고 확인하기
```javascript
// DiaryList.js
const DiaryList = ({diaryList}) =&gt; {
    console.log(diaryList)
    return &lt;div className=&quot;DiaryList&quot;&gt;
        &lt;h2&gt;일기 리스트&lt;/h2&gt;
    &lt;/div&gt;
}</code></pre><ul>
<li>콘솔에 출력된 <code>diaryList</code>는 우리가 <code>&lt;DiaryList diaryList={dummyList}/&gt;</code>로 넘겨준 일기가 담긴 배열
<img src="https://velog.velcdn.com/images/dev-sjko/post/76959544-75be-45c6-9a0f-d4df3f4e23ec/image.png" alt=""></li>
</ul>
<blockquote>
<p>*아래처럼 <code>prop</code>을 그대로 출력했을 때는 <code>diaryList</code>라는 key에 배열을 vaule로 갖는 객체가 출력됨(prop 그 자체!)</p>
</blockquote>
<pre><code class="language-javascript">const DiaryList = (prop) =&gt; {
console.log(prop)
}</code></pre>
<p><img src="https://velog.velcdn.com/images/dev-sjko/post/8aabe5e5-759a-4199-a366-8fb353cb5cbf/image.png" alt=""></p>
<hr>
<p>📌 map으로 배열의 각 요소의 속성들 꺼내서 <code>&lt;div&gt;&lt;/div&gt;</code> 안에 담고 화면에 그려주기</p>
<pre><code class="language-javascript">// DiaryList.js
const DiaryList = ({diaryList}) =&gt; {
    return &lt;div className=&quot;DiaryList&quot;&gt;
        &lt;h2&gt;일기 리스트&lt;/h2&gt;
        &lt;h4&gt;{diaryList.length}개의 일기가 있습니다.&lt;/h4&gt;
        &lt;div&gt;
            {diaryList.map((item)=&gt;{
                return &lt;div key={item.id}&gt;
                &lt;div&gt;작성자 : {item.author}&lt;/div&gt;
                &lt;div&gt;내용 : {item.content}&lt;/div&gt;
                &lt;div&gt;감정 : {item.emotion}&lt;/div&gt;
                &lt;div&gt;작성시간(ms) : {item.created_date}&lt;/div&gt;
                &lt;/div&gt;
            }) }
        &lt;/div&gt;
    &lt;/div&gt;
}</code></pre>
<ul>
<li>각 일기 요소의 최상위 태그인 div에 <code>&lt;div key={item.id}&gt;</code>로 고유한 key값을 만들어줌(<em>Each child should have a unique key prop</em> 에러 해결)<blockquote>
<p>*요소마다 고유한 id가 없다면 배열의 map의 콜백함수의 두 번째 인자인 배열의 <code>index</code>를 unique key값으로 사용해도 될 것 같지만,  나중에 일기 요소를 삭제, 수정, 추가해서 index의 순서가 바뀌었을 때 문제가 될 수 있음. =&gt; 지양하기!</p>
</blockquote>
</li>
</ul>
<hr>
<p>❓만약에 DiaryList 컴포넌트에 prop이 제대로 전달되지 않았다면? (<code>diaryList={undefined}</code>라면?)</p>
<blockquote>
<pre><code class="language-javascript">// DiaryList.js
DiaryList.defaultProps={
    diaryList: [],
}</code></pre>
</blockquote>
<pre><code>- `defaultProps` 를 이용해서 혹시나 컴포넌트에 데이터가 전달되지 않았을 때 사용할 기본값을 설정해주면 에러를 방지할 수 있다!

---
✏️** 일기 리스트에 렌더링되는 각각의 일기 아이템 별도의 파일로 분리시켜주기**
파일 : DiaryItem.js
```javascript
const DiaryItem = ({id, author, content, emotion, created_date}) =&gt; {
    return &lt;div className=&quot;DiaryItem&quot;&gt;
    &lt;div className=&quot;info&quot;&gt;
        &lt;span&gt;작성자 : {author} | 감정점수 : {emotion}&lt;/span&gt;
        &lt;br /&gt;
        &lt;span className=&quot;date&quot;&gt;
            {new Date(created_date).toLocaleString()}
        &lt;/span&gt;
    &lt;/div&gt;
    &lt;div className=&quot;content&quot;&gt;{content}&lt;/div&gt;
    &lt;/div&gt;
}

export default DiaryItem;</code></pre><p>파일 : DiaryList.js</p>
<pre><code class="language-javascript">import DiaryItem from &#39;./DiaryItem.js&#39;

const DiaryList = ({diaryList}) =&gt; {
    return &lt;div className=&quot;DiaryList&quot;&gt;
        &lt;h2&gt;일기 리스트&lt;/h2&gt;
        &lt;h4&gt;{diaryList.length}개의 일기가 있습니다.&lt;/h4&gt;
        &lt;div&gt;
            {diaryList.map((item)=&gt;{
               return &lt;DiaryItem key={item.id} {...item}/&gt;
            }) }
        &lt;/div&gt;
    &lt;/div&gt;
}

DiaryList.defaultProps={
    diaryList: [],
}
export default DiaryList;</code></pre>
<blockquote>
<p><code>{...item}</code>은 diaryList 배열의 각 요소인 객체(<code>{id:1, author:&quot;hailee&quot;, content:&quot;hi&quot;,..}</code>)를 펼쳐서 DiaryItem에 prop으로 전달해준 것!
=&gt; DiaryItem이 DiaryList.map에 의해 호출될 때 마다 각각의 요소가 갖는 속성들이 prop으로 넘어가고 그에 맞는 DiaryItem이 렌더링 됨</p>
</blockquote>
<hr>
<h4 id="studying">Studying...</h4>
<blockquote>
</blockquote>
<p><a href="https://inf.run/qAuJ">https://inf.run/qAuJ</a>
한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지 by 이정환 Winterlood</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[엘리스 SW 엔지니어 트랙 3기] 7~8주차 회고]]></title>
            <link>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0-78%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0-78%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 07 Nov 2022 17:05:47 GMT</pubDate>
            <description><![CDATA[<hr>
<p><img src="https://velog.velcdn.com/images/dev-sjko/post/945b9a9e-17c7-4d94-be05-86ab19e29dfa/image.png" alt="">
천사 스터디팀원 분이 나눠주신 자양강장제👼</p>
<hr>
<h3 id="잘한-점">잘한 점</h3>
<p>프로젝트 하면서 모르는 부분 제대로 공부한 거! 이번 1차 프로젝트는 엘리스에서 기본 코드를 제공해주었는데 로그인 기능을 구현하는 코드가 하나도 이해가 안돼서(jwt가 뭐죠..😢) 관련된 내용만 주말 내내 붙잡고 공부했다. 막상 공부할 때는 지금 급한 일들도 많은데 내가 이걸 보고있는 게 맞나 싶다가도 마침내 스켈레톤 코드가 이해 됐을 때는 진짜 감격 그 자체.. 어떻게 동작하는지 한 번 익히고 나니까 내가 원하는 방향으로 바꿔보기도 하고, 좀 더 효율적인 방법은 없을까 고민해보기도 하면서 정말 시간 가는 줄 모르고 코드 생각만 했던 것 같다. 아직 구현해야 할 것도, 해결해야 할 것도 많이 남아있지만 그래도 왠지 잘 끝낼 수 있을 것 같은 느낌..!😊</p>
<h3 id="개선할-점">개선할 점</h3>
<p>트러블 슈팅 기록 하기. 사실 프로젝트 하다보면 시간에 쫓겨서 에러가 생겼을 때 얼른 해결하고 다음 작업을 하는 경우가 훨씬 많긴 하지만..🥲 그래도 구글에 검색해보기 전에 터미널도 좀 더 꼼꼼히 보고 어떤 것 때문에 오류가 생겼는지 한 번 더 생각해보는 습관을 들여야 나중에도 어떻게 해결했는지 기억에 더 잘 남을 것 같다. </p>
<h3 id="목표">목표</h3>
<p>1차 프로젝트 무사히 완성하기!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[엘리스 SW 엔지니어 트랙 3기] 5~6주차 회고]]></title>
            <link>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0-6%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0-6%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 23 Oct 2022 13:43:27 GMT</pubDate>
            <description><![CDATA[<p>날이면 날마다.. 안오고 2주마다 오는 회고입니다.</p>
<hr>
<p><img src="https://velog.velcdn.com/images/dev-sjko/post/997b57dc-8675-4a3b-9104-8f7b282ef2b8/image.png" alt=""></p>
<p><code>비전공자 &amp;&amp; 엔젤</code>들만 모인 스터디🎶</p>
<hr>
<h3 id="잘한-점">잘한 점</h3>
<p>지난 2주 동안은 나름 이런저런 이벤트들이 많았다! 타입스크립트와 본격 백엔드 강의가 시작한 것도 그렇지만 1차 스터디 결과물 발표도 있었고, 취업지원 소개와 직무 특강도 들을 겸 성수에 있는 엘리스 훈련장도 처음으로 가봤다. 그 중에서도 가장 기억에 남는 건 스터디 발표😊 발표라는 걸 해본지가 어언..(먼산) 고작 3분 말하는 것도 혼자 온갖 시뮬레이션 돌려보면서 엄청 긴장했는데 팀원들의 응원이 너무너무너무 큰 힘이 되었다. 발표 무사히 끝낸 것이 이번 달 가장 잘한 일이라는 생각✨</p>
<h3 id="개선할-점">개선할 점</h3>
<p>이번에 취업지원 소개 때 엘리스 매니저님들을 처음으로 뵈었는데 &quot;경력직으로 들어가는 거 아니니까 걱정할 시간에 하나라도 더 보고 공부하세요!&quot; 라는 말에 아주 뼈를 맞아버렸다..🥲  미래 걱정은 조금만 덜 하고 당장 눈앞에 있는 일들을 차근차근 해나가야지. (강의 진도 미루지 말고 듣기.. 라던가...)</p>
<h3 id="목표">목표</h3>
<p>그날 진도율은 그날 채우기..!  </p>
<hr>
<p><img src="https://velog.velcdn.com/images/dev-sjko/post/fc2fa813-7094-470e-bac7-0390a61a34c5/image.jpg" alt="">
낙낙.. 너무 좋아여... 💜</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[엘리스 SW 엔지니어 트랙 3기] 3~4주차 회고]]></title>
            <link>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0-4%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0-4%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 09 Oct 2022 15:12:22 GMT</pubDate>
            <description><![CDATA[<p>어쩌다보니 2주 주기로 돌아오는 회고</p>
<hr>
<p><img src="https://velog.velcdn.com/images/dev-sjko/post/4c34491d-6b4e-4768-acd6-01e76d76ddd7/image.png" alt=""></p>
<p>따수운 튜터님의 응원.. </p>
<hr>
<h3 id="잘한-점">잘한 점</h3>
<p>포기하지 않은 거..?ㅎㅎ 사실 4주차에 본격적으로 비동기 관련 내용을 배우기 시작하면서 실습 문제를 건드리지도 못하는 사태가 발생했고 우울에 빠지기 직전이었는데 튜터님 메세지에 진짜 눈물이 차올라서 고갤 들어....ㅠㅠㅠㅠ 이거 보고 &#39;나만 어렵고 힘든 게 아니었구나&#39; 하고 많이 위로받았다. 지금까지는 모르는 개념이 나왔을 때 완벽하게 숙지하고 넘어가지 않으면 불안해서 계속 붙잡고 있었는데 이제 &#39;익숙해진다&#39;에 더 초점을 맞춰서 공부할 수 있을 것 같다!<del>(잘한 점이 아니라 반성같은데..?)</del></p>
<h3 id="개선할-점">개선할 점</h3>
<p>체력 관리.. 체력 관리...! 잠을 좀 자!😞</p>
<h3 id="목표">목표</h3>
<p>책을 읽자</p>
<hr>
<p>이제 대망의 백엔드..!🤯</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Iteration]]></title>
            <link>https://velog.io/@dev-sjko/Iteration</link>
            <guid>https://velog.io/@dev-sjko/Iteration</guid>
            <pubDate>Sun, 09 Oct 2022 13:29:18 GMT</pubDate>
            <description><![CDATA[<h1 id="iteration">Iteration</h1>
<p>반복 가능한 객체를 순회한다. </p>
<ul>
<li>Iterable한 객체란 반복 가능한 객체를 의미</li>
<li>Iterable한 객체는 <code>for...of</code>, <code>spread 연산자</code>, <code>구조분해 할당</code> 등을 사용할 수 있음 e.g. Array, String, NodeList, Map, Set</li>
</ul>
<blockquote>
<pre><code class="language-javascript">const array = [1, 2, 3];
for (const item of array) {
    console.log(item);
} 
// 1 2 3 이 한줄씩 출력됨</code></pre>
</blockquote>
<pre><code>for...of 반복문의 장점
- 어떤 데이터를 가져오려는지 명시적으로 나타낼 수 있어서 단순 for문 보다 가독성이 좋음
- array의 length에 대한 정보 없이 배열만으로 순회가 가능

---

✍ 어떤 객체가 iterable한 객체인걸 정확히 어떻게 알 수 있냐? 
⇒ **`Symbol.iterator`**를 먼저 알아야 한다.

#### `Symbol.iterator` 확인하기
1. `in` 연산자 사용
```javascript
const array = [1, 2, 3];
console.log(Symbol.iterator in array); 
// array의 속성 중에서 Symbol.iterator라는 것이 있냐
// true</code></pre><ol start="2">
<li><code>array.values()</code> 사용<pre><code class="language-javascript">const arrIterator= array.values(); 
console.log(arrIterator);
// [Array Iterator] {} =&gt; Array Iterator 라는 객체를 반환함</code></pre>
</li>
</ol>
<ol start="3">
<li>array의 property로 찾아보기<pre><code class="language-javascript">const arrIterator = array[Symbol.iterator](); 
// const length = array[&#39;length&#39;]; 이렇게 array의 property를 찾는거랑 같은 형식
// array[Symbol.iterator]는 function이기 때문에 ()로 실행시켜줌
console.log(arrIterator); 
// [Array Iterator] {} =&gt; Array Iterator 라는 객체를 반환함</code></pre>
</li>
</ol>
<p>*<em>결론 : <code>Symbol.iterator</code>는 array에 있는 속성 중 하나이다. *</em></p>
<hr>
<p><img src="https://velog.velcdn.com/images/dev-sjko/post/bb44e507-711e-4791-a228-d761e0b655b1/image.png" alt=""></p>
<p>콘솔에서 확인해보니 Array Iterator 객체에는 <strong><code>next</code></strong>라는 메소드가 있음</p>
<blockquote>
<p>&quot;그러면 <code>arrIterator</code>는 어쨌든 객체이고 그 객체가 <code>next</code>라는 메소드를 갖고 있다면 <strong><code>arrIterator.next();</code></strong>로 실행할 수 있다는거지?&quot;</p>
</blockquote>
<p>✍ 실행해보자!</p>
<pre><code class="language-javascript">console.log(arrIterator.next()); // { value: 1, done: false }
console.log(arrIterator.next()); // { value: 2, done: false }
console.log(arrIterator.next()); // { value: 3, done: false }
console.log(arrIterator.next()); // { value: undefined, done: true }
// value와 done이라는 프로퍼티를 가진 object가 반환됨</code></pre>
<ul>
<li><strong><code>value</code></strong> : 최근 순회한 collection의 요소 </li>
<li><strong><code>done</code></strong> : 반복할 요소가 더 이상 없는지? 반복이 다 끝났는지?</li>
</ul>
<p>반복 가능한 객체에서 <code>for… of</code>를 쓰면 이 Iterator를 찾아내서 <code>next</code> 메소드를 계속 호출한다고 보면 된다. 그렇게 계속 실행하면서 <code>value</code>의 값을 받아서 출력해주다가 <code>done</code> 속성값이 true가 되면 break로 빠져나온다.</p>
<blockquote>
</blockquote>
<p>동작 원리를 풀어서 보면 아래와 같은 식으로 된다고 생각하면 된다.</p>
<pre><code class="language-javascript">while (true) {
    const item = iterator.next();
    if (item.done) break;
    console.log(item.value);
}</code></pre>
<p>📌 iterable한 객체란 이 <code>Symbol.iterator</code>라는 속성을 갖고 있어서 반복이 가능한 객체를 뜻한다.
=&gt; 다시 말해, <code>next</code> method에서 <code>{ value : , done : }</code>(Iterator 객체)을 return하는 <code>Symbol.iterator</code>를 갖고 있다면 for… of나 spread 연산자, 구조분해할당 등을 사용할 수 있다!</p>
<hr>
<blockquote>
<p>📌 <code>values()</code> 메서드는 배열의 각 인덱스에 대한 값을 가지는 새로운 Array Iterator 객체를 반환한다. Array Iterator에는 이미 그 배열에 대한 값들이 <code>next().value</code> 로 들어있다.
따라서 해당 배열을 for…of로 돌릴 수도 있지만 Array Iterator를 for…of로 돌릴 수도 있다.
(MDN 설명)</p>
</blockquote>
<pre><code class="language-javascript">// 확인해보기
const array1 = [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;];
const arrVal = array1.values(); // iterator 반환
for (const value of array1.values()) {
  if (value === arrVal.next().value) {
    console.log(&#39;yes&#39;);
  } else {
    console.log(&#39;no&#39;)
  }
  console.log(value);
}</code></pre>
<hr>
<h3 id="for-in과-for-of의-차이">for… in과 for …of의 차이</h3>
<ul>
<li><code>for… of</code>는 <strong>Symbol.iterator</strong>를 갖고 있는 array나 NodeList, Set 등에서만 쓸 수 있다. (iterator의 <code>next().value</code>값을 가져와야하기 때문에) ⇒ 일반 object에서는 사용 못함</li>
<li><code>for… in</code>은 object와 array 둘 다에서 쓸 수 있으며 object에서는 key값을 가져오고 array에서는 index값을 가져옴<pre><code class="language-javascript">const object = { a: 1, b: 2, c: 3 };
</code></pre>
</li>
</ul>
<p>for (const property in object) {
  console.log(<code>${property}: ${object[property]}</code>);
}</p>
<p>// expected output:
// &quot;a: 1&quot;
// &quot;b: 2&quot;
// &quot;c: 3&quot;
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[엘리스 SW 엔지니어 트랙 3기] 1~2주차 회고]]></title>
            <link>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0-2%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0-2%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Fri, 23 Sep 2022 14:00:14 GMT</pubDate>
            <description><![CDATA[<p>지난 2주를 돌아봅니다!(주절주절)</p>
<hr>
<p><img src="https://velog.velcdn.com/images/dev-sjko/post/f36677d1-9dc5-4086-9c0f-d8994fc0ffe6/image.png" alt="">
내 처참한 노션.. </p>
<hr>
<h3 id="잘한-점">잘한 점</h3>
<p>수업 들으면서 다 정리는 못해도 모르는 부분이나 생소한 개념은 메모라도 해놨다가 나중에 꼭 찾아보는 거!(봐야할 메모가 오조오억 개인게 문제이긴 한데) 혼자 천천히 읽어보고 다른 문서도 찾아보면서 정리하고 다시 수업 자료를 보면 완전 잘 이해됨.. 그렇게 복습하다보면 코치님들이 준비하신 수업 자료가 정말 중요한 개념들만 최대한 이해하기 쉽게 꾹꾹 눌러 담은 것들이라는게 느껴진다. (그걸 내가 보자마자 이해를 못하는게 문제이긴 한데) 
&quot;이건 나중에 배울거예요~&quot;라고 하시는 개념도 그냥 지나쳤다가 그때가서 아예 처음 머리에 넣는 것과 지금 대충이라도 이해해뒀다가 설명을 한 번 더 듣는 것은 천지차이라는 걸 많이 느끼고 있다.</p>
<h3 id="개선할-것">개선할 것</h3>
<p>나의.. 멘탈?😂 사실 실습 문제들 풀면서 자괴감을 많이 느꼈는데 그동안 여기에 자바스크립트 공부 내용을 정리해왔다는 것이 어이가 없을 정도로 내가 지금까지 그냥 공부하는 척만 해왔다는 사실을 1분마다 깨달았다..^^^ 내가 온갖 방법을 다 쓰면서 하면 절대 실행 안되는데 코치님이 하신거 보면 코드 두 줄.. 안그래도 공부할 시간 없는데 오타 하나 때문에 30분, 40분 날리면 또 억장 와르르.. 공부라는 게 내가 오늘 10시간 한다고 해서 바로 내일 모든 걸 통달할 수 있는 건 절대 아니라는 걸 알고는 있었지만 그래도 욕심만큼 내 머리가 따라주지 않으니 자꾸 우울해진다. 하지만 뭐 어떡할거야 그냥 믿고 해야지..!</p>
<h3 id="목표">목표</h3>
<p>아예 몰랐던 개념 잘 정리해서 벨로그에 포스팅하기! </p>
<hr>
<p><code>어.. 할 수 있을 것 같은데?</code> 와 <code>아.. 안될 것 같은데?</code> 의 대환장콜라보의 나날들이지만 앞으로 갈 길이 멀다👊</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[엘리스 SW 엔지니어 트랙 3기]]></title>
            <link>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0</link>
            <guid>https://velog.io/@dev-sjko/%EC%97%98%EB%A6%AC%EC%8A%A4-SW-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%ED%8A%B8%EB%9E%99-3%EA%B8%B0</guid>
            <pubDate>Sat, 10 Sep 2022 15:20:56 GMT</pubDate>
            <description><![CDATA[<p>독학을 하면서 나를 잡아줄 커리큘럼의 필요성을 점점 느끼던 비전공자1..
여러 부트캠프들을 눈여겨보던 중 기다리던 엘리스의 SW 엔지니어 트랙 3기 공고가 열렸고 간절한 마음으로 지원했다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/dev-sjko/post/d1676764-6a84-4397-b034-75773f394786/image.png" alt=""></p>
<p>서류 제출한지 하루밖에 안지났는데 바로 합격 메일 와서 놀랐다! </p>
<hr>
<p><img src="https://velog.velcdn.com/images/dev-sjko/post/3c8d9ab2-8f6a-4935-afff-0ac5cbd589e2/image.png" alt=""></p>
<p>프리트랙에는 강의 뿐만 아니라 역량 테스트를 위한 연습 문제도 포함되어 있었는데 처음에는 문제도 이해 못하겠고 아예 어떻게 시작해야 할지조차 몰라서 멘탈이 많이 흔들렸다..😢 풀지 못하면 눈에 익히기라도 하자! 하면서 테스트 전날까지 계속 돌려봤음.. </p>
<p>역량 테스트는 하루 동안 원하는 시간에 접속해서 볼 수 있었다. 코딩 테스트가 더 어려울 것 같아서 먼저 보고 논리력 퀴즈 들어갔는데 진짜.. (말잇못</p>
<hr>
<p>역량 테스트 이후에 당연히 떨어졌겠지 하고 손 놓고 있었는데 합격 문자 받고 인터뷰 맹준비함🤯</p>
<blockquote>
<p>실제로 받은 질문은 </p>
</blockquote>
<ol>
<li>개발에 관심을 갖게 된 계기</li>
<li>개발자가 되기 위해 지금까지 한 노력</li>
<li>팀프로젝트에서 팀원이 참여를 안한다면 어떻게 할건지</li>
<li>하고 싶은 말이나 질문</li>
</ol>
<p>비대면 면접은 처음이라 엄청 떨렸던 기억 뿐.. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/dev-sjko/post/46fb6ada-5dc5-4b50-b893-8ec9ed31087a/image.png" alt="">
떨어져도 너무 실망하지 말자고 마음 다잡고 있었는데 합격 메일 받고 소리지를 뻔ㅠㅠ 이제 뒤돌아보지 말고 4개월 열심히 달려보자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React useRef]]></title>
            <link>https://velog.io/@dev-sjko/React-useRef</link>
            <guid>https://velog.io/@dev-sjko/React-useRef</guid>
            <pubDate>Sat, 10 Sep 2022 13:47:10 GMT</pubDate>
            <description><![CDATA[<h3 id="📌-react에서-dom-조작하기">📌 React에서 DOM 조작하기</h3>
<p>목표: 저장 버튼을 클릭했을 때 정상적으로 입력되지 않았으면 focus하기</p>
<p>✍ 정상적으로 입력되지 않은 element에 접근하기</p>
<p><code>useRef</code> : React.MutableRefObject를 반환함. HTML 요소에 접근할 수 있는 기능.</p>
<pre><code class="language-javascript">const DiaryEditor = () =&gt; {

    const authorInput = useRef();
    const contentsInput = useRef();

    const handleSubmit = () =&gt;{
        if(state.author.length &lt; 1){
            authorInput.current.focus();
            // ref가 authorInput인 태그에 접근 -&gt; current로 현재 태그 가져옴
            return; // 더이상 진행 안되도록 동작 끝냄
        }
        if(state.contents.length &lt; 3){
            contentsInput.current.focus();
            return;
        }
        alert(&#39;저장 성공!&#39;);
    }

    return &lt;div className=&quot;DiaryEditor&quot;&gt;
        &lt;h2&gt;오늘의 일기&lt;/h2&gt;
        &lt;div&gt;
            &lt;input ref={authorInput} /&gt;
        &lt;/div&gt;
        &lt;div&gt;
            &lt;textarea ref={contentsInput} /&gt; 
        &lt;/div&gt;</code></pre>
<ul>
<li><code>const authorInput = useRef();</code> 레퍼런스 객체 생성</li>
<li><code>&lt;input ref={authorInput} /&gt;</code> 지켜볼 요소에 ref 속성 추가</li>
<li><code>authorInput.current</code> 현재 요소 불러옴</li>
<li><code>focus();</code> 원하는 이벤트 추가</li>
</ul>
<hr>
<h4 id="studying">Studying...</h4>
<blockquote>
</blockquote>
<p><a href="https://inf.run/qAuJ">https://inf.run/qAuJ</a>
한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지 by 이정환 Winterlood</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 사용자 입력 처리하기]]></title>
            <link>https://velog.io/@dev-sjko/React-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9E%85%EB%A0%A5-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dev-sjko/React-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9E%85%EB%A0%A5-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 06 Sep 2022 14:48:12 GMT</pubDate>
            <description><![CDATA[<h3 id="📌-입력받은-값을-추후-사용할-수-있도록-usestate를-이용해-저장해보자">📌 입력받은 값을 추후 사용할 수 있도록 useState를 이용해 저장해보자</h3>
<p>✍** 작성자 이름 입력받기**</p>
<pre><code class="language-javascript">import {useState} from &quot;react&quot;;

const DiaryEditor = () =&gt; {

    const [author, setAuthor] = useState(&#39;&#39;);

    return &lt;div className=&quot;DiaryEditor&quot;&gt;
        &lt;h2&gt;오늘의 일기&lt;/h2&gt;
        &lt;div&gt;
            &lt;input value={author}/&gt;
        &lt;/div&gt;
    &lt;/div&gt;;
}; 

export default DiaryEditor;</code></pre>
<p><em>*<code>class 이름</code> = <code>컴포넌트 이름</code> : 나중에 class를 이용해서 css로 스타일링 할 때 직관적으로 보기 위해(참고용 방법!)</em></p>
<ul>
<li>input의 value를 state로 설정했을 때 
: 해당 state는 <code>setAuthor</code> 함수로만 변경될 수 있기 때문에 input이 변경될 때 <code>setAuthor</code> 함수를 이용해서 입력한 값이 input에 보이도록 해줘야함. </li>
</ul>
<hr>
<p>➡️ onChange 속성 사용하기</p>
<pre><code class="language-javascript">    return &lt;div className=&quot;DiaryEditor&quot;&gt;
        &lt;h2&gt;오늘의 일기&lt;/h2&gt;
        &lt;div&gt;
            &lt;input value={author} onChange={(e)=&gt;{
                console.log(e);
            }}/&gt;
        &lt;/div&gt;
    &lt;/div&gt;;</code></pre>
<ul>
<li>input 태그에 Change 이벤트가 발생하면 콜백함수 `{(e)=&gt;{<pre><code>          console.log(e);
      }}`를 실행하도록 명령</code></pre></li>
<li>input에 값을 입력할 때마다 event 객체에 대한 정보 출력됨</li>
<li>event 객체의 target-value를 이용해서 input의 값 변경 가능</li>
</ul>
<pre><code>    return &lt;div className=&quot;DiaryEditor&quot;&gt;
        &lt;h2&gt;오늘의 일기&lt;/h2&gt;
        &lt;div&gt;
            &lt;input value={author} onChange={(e)=&gt;{
                setAuthor(e.target.value);
            }}/&gt;
        &lt;/div&gt;
    &lt;/div&gt;;</code></pre><ul>
<li>input 태그에 Change 이벤트가 발생하면 author의 값을 e.target.value로 변경
(setAuthor를 사용해 input에 입력한 값으로 author 값을 업데이트)</li>
</ul>
<hr>
<p>작성자 이름 입력하는 <code>input</code>과 일기 내용 입력하는 <code>textarea</code>는 동작 방식이 동일
: value={state}, setState 함수를 이용해 event.target.value로 state 변경, 자료형은 문자열</p>
<p><strong>✍ 동작이 비슷한 state 하나로 묶어주기</strong></p>
<pre><code class="language-javascript">const DiaryEditor = () =&gt; {
    const [state, setState] = useState(
        {
            author:&quot;&quot;,
            contents:&quot;&quot;,
        } 
    )

    return &lt;div className=&quot;DiaryEditor&quot;&gt;
        &lt;h2&gt;오늘의 일기&lt;/h2&gt;
        &lt;div&gt;
            &lt;input value={state.author} onChange={(e)=&gt;{
                setState({
                    author: e.target.value,
                    contents:state.contents,
                });
            }}/&gt;
        &lt;/div&gt;
        &lt;div&gt;
            &lt;textarea value={state.contents} onChange={(e)=&gt;{
                setState({
                    author: state.author,
                    contents: e.target.value,
                })}}/&gt; 
        &lt;/div&gt;
    &lt;/div&gt;;
}; </code></pre>
<ul>
<li>state의 초기값을 객체로 만들어줌</li>
<li>각 target에 onChange 이벤트 발생 =&gt; setState 콜백함수 실행 =&gt; 각각 event.target.value로 변경할 state 업데이트(자신의 change가 아닌 경우는 원래 값으로 고정) =&gt; <code>state.author</code> 혹은 <code>state.contents</code>로 value 변경</li>
</ul>
<p>✍ 만약 입력창이 2개가 아니라 10개였다면..?</p>
<pre><code class="language-javascript">const DiaryEditor = () =&gt; {
    const [state, setState] = useState(
        {
            author:&quot;&quot;,
            contents:&quot;&quot;,
        }
    )

    return &lt;div className=&quot;DiaryEditor&quot;&gt;
        &lt;h2&gt;오늘의 일기&lt;/h2&gt;
        &lt;div&gt;
            &lt;input value={state.author} onChange={(e)=&gt;{
                setState({
                    ...state,
                    author: e.target.value,
                         });
            }}/&gt;
        &lt;/div&gt;
        &lt;div&gt;
            &lt;textarea value={state.contents} onChange={(e)=&gt;{
                setState({
                    ...state,
                    contents: e.target.value,
                })}}/&gt; 
        &lt;/div&gt;
    &lt;/div&gt;;
}; </code></pre>
<p>📌 Spread 연산자로 state 객체를 펼친 후 업데이트 할 state의 property만 써주기</p>
<blockquote>
<p>❌ <code>...state</code>를 업데이트 할 속성 뒤에 쓴다면?</p>
</blockquote>
<pre><code>setState({
author: e.target.value,
...state,
     });</code></pre><ul>
<li>author 값 변경 후 state 객체 펼쳐짐 =&gt; 원래 state 객체에 있던 <code>state.author</code> 값으로 author 값이 다시 변경 =&gt; 업데이트 안됨</li>
</ul>
<hr>
<p>✍ onChange에 전달할 함수도 하나로 합쳐보자</p>
<pre><code class="language-javascript">const DiaryEditor = () =&gt; {
    const [state, setState] = useState(
        {
            author:&quot;&quot;,
            contents:&quot;&quot;,
        }
    )

    const handleChangeState = (e) =&gt; {
        setState({
            ...state,
            [e.target.name] : e.target.value,
        })
    }

    return &lt;div className=&quot;DiaryEditor&quot;&gt;
        &lt;h2&gt;오늘의 일기&lt;/h2&gt;
        &lt;div&gt;
            &lt;input name=&quot;author&quot; value={state.author} onChange={handleChangeState}/&gt;
        &lt;/div&gt;
        &lt;div&gt;
            &lt;textarea name=&quot;contents&quot; value={state.contents} onChange={handleChangeState}/&gt; 
        &lt;/div&gt;
    &lt;/div&gt;;
}; // 각 입력창에 name 추가해줌</code></pre>
<ul>
<li><code>e.target.name</code>은 각 입력창의 이름이자 state 객체의 속성의 key값</li>
<li>각 target에 onChange 이벤트 발생 =&gt; handleChangeState 함수 실행 =&gt; 
원래 state 객체 펼치고 <code>[e.target.name] : e.target.value</code> 변경</li>
<li><code>[e.target.name]</code>은 객체의 괄호 표기법</li>
</ul>
<hr>
<p>✍ 감정 점수도 같은 이벤트 핸들러로 만들어주기</p>
<pre><code>    return &lt;div className=&quot;DiaryEditor&quot;&gt;
        &lt;h2&gt;오늘의 일기&lt;/h2&gt;
        &lt;div&gt;
            &lt;input name=&quot;author&quot; value={state.author} onChange={handleChangeState}/&gt;
        &lt;/div&gt;
        &lt;div&gt;
            &lt;textarea name=&quot;contents&quot; value={state.contents} onChange={handleChangeState}/&gt; 
        &lt;/div&gt;
        &lt;div&gt;
        &lt;label&gt;오늘의 감정점수 : &lt;/label&gt;
            &lt;select name=&quot;emotion&quot; value={state.emotion} onChange={handleChangeState}&gt;
                &lt;option value={1}&gt;1&lt;/option&gt;
                &lt;option value={2}&gt;2&lt;/option&gt;
                &lt;option value={3}&gt;3&lt;/option&gt;
                &lt;option value={4}&gt;4&lt;/option&gt;
                &lt;option value={5}&gt;5&lt;/option&gt;
            &lt;/select&gt;
        &lt;/div&gt;
        &lt;div&gt;
            &lt;button onClick={handleSubmit}&gt;일기 저장하기&lt;/button&gt;
        &lt;/div&gt;
    &lt;/div&gt;;</code></pre><hr>
<h4 id="studying">Studying...</h4>
<blockquote>
</blockquote>
<p><a href="https://inf.run/qAuJ">https://inf.run/qAuJ</a>
한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지 by 이정환 Winterlood</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript 응용(API 호출)]]></title>
            <link>https://velog.io/@dev-sjko/API-%ED%98%B8%EC%B6%9C</link>
            <guid>https://velog.io/@dev-sjko/API-%ED%98%B8%EC%B6%9C</guid>
            <pubDate>Tue, 06 Sep 2022 12:55:17 GMT</pubDate>
            <description><![CDATA[<p>API 호출 : client에서 필요한 정보를 server에 요청하고 응답을 받아오기 위해 하는 동작</p>
<p>📌 API 호출을 비동기적으로 처리해야하는 이유?
정보를 요청할 서버에서 언제 데이터를 받을 수 있을지 정확하지 않기 때문에 작업의 성공과 실패를 확인할 수 있는 Promise와 async, await을 사용함</p>
<pre><code class="language-javascript">let rawResponse = fetch(&quot;https://jsonplaceholder.typicode.com/posts&quot;);

rawResponse.then((res) =&gt; console.log(res));
// Response {type: &quot;cors&quot;, url: &quot;https://jsonplaceholder.typicode.com/posts&quot;, redirected: false…}
// fetch의 결과값으로 Response 객체가 출력됨
// Response 객체는 쉽게 말해서 우리가 원하는 데이터가 담겨있는 포장지</code></pre>
<ul>
<li><strong><code>fetch</code></strong> : JS 내장함수. API를 호출할 수 있게 하는 기능.
인자로 정보를 요청할 URL(가져오고자 하는 리소스의 경로)을 받고 <strong>Promise를 반환함</strong> =&gt; then 메소드를 사용해서 결과값을 이용할 수 있음</li>
<li>우리가 원하는 정보는 JSON 형식으로 되어있는 객체 배열</li>
</ul>
<pre><code class="language-javascript">async function getData() {
  let rawResponse = await fetch(&quot;https://jsonplaceholder.typicode.com/posts&quot;);
  let jsonResponse = await rawResponse.json();
  console.log(jsonResponse);
}

getData();
// (100) [Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, …]</code></pre>
<hr>
<p>API 호출 내용은 봐도봐도 부족한 느낌ㅠㅠ 중요한 부분이니 연습하며 꼭 숙지하기.. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React State, Props]]></title>
            <link>https://velog.io/@dev-sjko/State-Props</link>
            <guid>https://velog.io/@dev-sjko/State-Props</guid>
            <pubDate>Mon, 05 Sep 2022 08:56:06 GMT</pubDate>
            <description><![CDATA[<p>✍** Counter 만들어주기**</p>
<p>파일 : Counter.js</p>
<pre><code class="language-javascript">import React, {useState} from &quot;react&quot;;
// useState는 react의 메소드이기 때문에 따로 import 해줘야함

const Counter = () =&gt;{
    const [count, setCount] = useState(0);
    const onIncrease = () =&gt; {
        setCount(count + 1);
    }
    const onDecrease = () =&gt; {
        setCount(count - 1);
    }
    return (
        &lt;div&gt;
            &lt;h2&gt;{count}&lt;/h2&gt;
            &lt;button onClick={onIncrease}&gt;+&lt;/button&gt;
            &lt;button onClick={onDecrease}&gt;-&lt;/button&gt;
        &lt;/div&gt;
    )
}

export default Counter;</code></pre>
<hr>
<p>✍ <strong>부모 컴포넌트에서 자식 컴포넌트로 정보를 전달해주고 싶을 때?</strong></p>
<p>파일 : App.js</p>
<pre><code class="language-javascript">import React from &#39;react&#39;;
import Counter from &#39;./Counter&#39;;
// import &#39;./App.css&#39;;

import MyHeader from &#39;./MyHeader&#39;;

function App() {
  const counterProps = {
    a:1,
    b:2,
    c:3,
    initialValue:6,
  }
  return (
&lt;div&gt;
&lt;MyHeader/&gt;
&lt;Counter {...counterProps}/&gt;
// Counter 컴포넌트에 spread 연산자로 Props 전달
&lt;/div&gt;
  );
}

export default App;</code></pre>
<p>파일 : Counter.js</p>
<pre><code class="language-javascript">const Counter = ({initialValue}) =&gt;{
// 객체의 비구조화 할당
// { initialValue } = counterProps
// counterProps 객체에서 key가 initialValue인 속성의 값을 initialValue로 활용할 수 있음
    const [count, setCount] = useState(initialValue);
    const onIncrease = () =&gt; {
        setCount(count + 1);
    }
    const onDecrease = () =&gt; {
        setCount(count - 1);
    }
    return (
        &lt;div&gt;
            &lt;h2&gt;{count}&lt;/h2&gt;
            &lt;button onClick={onIncrease}&gt;+&lt;/button&gt;
            &lt;button onClick={onDecrease}&gt;-&lt;/button&gt;
        &lt;/div&gt;
    )
}</code></pre>
<hr>
<blockquote>
<p>✍ 에러 핸들링 : App 컴포넌트에서 Counter 컴포넌트에게 전해주는 <code>counterProps</code> 객체에 <code>initialValue</code> 속성이 없을 때 기본값 설정하기</p>
</blockquote>
<p>파일 : Counter.js</p>
<pre><code class="language-javascript">const Counter = ({initialValue}) =&gt;{
    const [count, setCount] = useState(initialValue);
    const onIncrease = () =&gt; {
        setCount(count + 1);
    }
    const onDecrease = () =&gt; {
        setCount(count - 1);
    }
    return (
        &lt;div&gt;
            &lt;h2&gt;{count}&lt;/h2&gt;
            &lt;button onClick={onIncrease}&gt;+&lt;/button&gt;
            &lt;button onClick={onDecrease}&gt;-&lt;/button&gt;
        &lt;/div&gt;
    )
}
Counter.defaultProps={
    initialValue: 0,
}</code></pre>
<ul>
<li><code>defaultProps</code> 기능을 사용하면 전달받지 못한 props의  기본값을 설정해서 에러를 방지할 수 있음</li>
</ul>
<hr>
<p>✍ <strong>props로 자식 컴포넌트에게 동적인 데이터 전달하기</strong>
동적인 데이터 대표) state</p>
<p>파일: Counter.js</p>
<pre><code class="language-javascript">import React, {useState} from &quot;react&quot;;
import OddEvenResult from &quot;./OddEvenResult&quot;;

const Counter = ({initialValue}) =&gt;{
    const [count, setCount] = useState(initialValue);
    const onIncrease = () =&gt; {
        setCount(count + 1);
    }
    const onDecrease = () =&gt; {
        setCount(count - 1);
    }
    return (
        &lt;div&gt;
            &lt;h2&gt;{count}&lt;/h2&gt;
            &lt;button onClick={onIncrease}&gt;+&lt;/button&gt;
            &lt;button onClick={onDecrease}&gt;-&lt;/button&gt;
            &lt;OddEvenResult count={count}/&gt;
              //OddEvenResult 컴포넌트에 count 값을 props로 전달해줌
        &lt;/div&gt;
    )
}</code></pre>
<p>파일: OddEvenResult.js</p>
<pre><code class="language-javascript">const OddEvenResult = ({count}) =&gt; {
    // Counter.js의 count값을 props로 받아오기
    return &lt;&gt;{count % 2 === 0? &#39;짝수&#39; : &#39;홀수&#39;}&lt;/&gt;
}

export default OddEvenResult;</code></pre>
<ul>
<li>동적으로 변하는 state(<code>count</code> 값)에 따라 각각 다른 결과를 렌더하는 컴포넌트</li>
</ul>
<blockquote>
</blockquote>
<h2 id="📌-컴포넌트가-rerender-되는-경우">📌 컴포넌트가 rerender 되는 경우</h2>
<p>파일: OddEvenResult.js</p>
<pre><code class="language-javascript">const OddEvenResult = ({count}) =&gt; {
    console.log(&#39;RENDER!&#39;);
    return &lt;&gt;{10 % 2 === 0? &#39;짝수&#39; : &#39;홀수&#39;}&lt;/&gt;
    // 리턴값이 변하지 않는 자식 컴포넌트
}
export default OddEvenResult;</code></pre>
<ul>
<li>본인이 관리하는 state가 바뀔때마다 rerender</li>
<li>나에게 내려오는 props가 바뀔때마다 rerender</li>
<li>내 부모가 rerender되면 나도 rerender</li>
</ul>
<hr>
<p>✍** props로 다른 컴포넌트 전달하기**</p>
<p>여백을 주는 컴포넌트를 전달해보자</p>
<p>파일: Container.js</p>
<pre><code class="language-javascript">const Container = ({children}) =&gt;{
    return &lt;div style={{margin:20, padding:20, border:&#39;1px solid grey&#39;}}&gt;
        {children}
    &lt;/div&gt;
}

export default Container;</code></pre>
<p>파일: App.js</p>
<pre><code class="language-javascript">function App() {
  const counterProps = {
    initialValue: 0,
  }
  return (
    &lt;Container&gt;
&lt;div&gt;
&lt;MyHeader/&gt;
&lt;Counter {...counterProps}/&gt;
&lt;/div&gt;
    &lt;/Container&gt;
  ); // Container 컴포넌트로 나머지 요소들을 감싸주기
}</code></pre>
<p><img src="https://velog.velcdn.com/images/dev-sjko/post/49ddbe06-256e-4a3c-86f5-1505a9964460/image.png" alt=""></p>
<ul>
<li>App 컴포넌트에서 Container 컴포넌트의 자식 요소들은 Container 컴포넌트에 <code>children</code>이라는 prop으로 전달됨</li>
<li>children을 이용해서 자식 요소들을 값처럼 활용할 수 있음</li>
<li><code>children</code>을 console에 출력해보면 <code>react.element</code> 출력</li>
</ul>
<hr>
<h4 id="studying">Studying...</h4>
<blockquote>
</blockquote>
<p><a href="https://inf.run/qAuJ">https://inf.run/qAuJ</a>
한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지 by 이정환 Winterlood</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Create React App & JSX]]></title>
            <link>https://velog.io/@dev-sjko/Create-React-App</link>
            <guid>https://velog.io/@dev-sjko/Create-React-App</guid>
            <pubDate>Tue, 23 Aug 2022 14:33:28 GMT</pubDate>
            <description><![CDATA[<p>Why React?</p>
<ul>
<li>React는 <strong>컴포넌트 기반의 UI 라이브러리</strong>
공통으로 사용되는 HTML 요소를 컴포넌트로 만들면 수정하고 재사용하기 쉬워짐</li>
<li><strong>선언형 프로그래밍</strong> : 절차를 하나하나 설명하는 명령형 프로그래밍이 아닌 결론을 바로 알 수 있도록 코드를 작성하는 방법</li>
<li><strong>Virtual DOM을 사용</strong>
DOM : 문서 객체 모델. 브라우저가 HTML을 해석해서 화면에 보여줄 때 쉽게 해석하기 위해 트리 형태로 변형시킨 객체. </li>
<li>Javascript는 이러한 DOM을 통해 HTML로 짜여진 요소들을 생성 및 변형하고 삭제할 수 있음.
일반 JS만 사용한 브라우저는 화면의 요소가 변경될 때마다 렌더링의 모든 과정을 거쳐서 페이지를 불러와야함
React는 현재 가상 돔과 새 가상돔을 비교해서 변화하는 부분을 수정하고 모든 수정이 끝나면 일괄로 합쳐서 실제 돔에서 업데이트 할 수 있도록 던져줌
<del>변환된 DOM이 표시되는 과정?</del></li>
</ul>
<hr>
<blockquote>
<p>📌 React를 도와주는 라이브러리(패키지)들: Webpack, Babel
_*Webpack:  모듈 번들러. 다수의 JS 파일을 하나의 파일로 합쳐주는 역할.
Babel: JS 컴파일러. JSX 등의 쉽고 직관적인 JS 문법을 사용할 수 있도록 도와주는 역할.
_</p>
</blockquote>
<p><strong>Create React App</strong>: React의 boiler plate.
*Bolier Plate: 이미 세팅 완료된 패키지. 프로그램을 만드는 데 필요한 패키지들을 모아놓은 패키지. </p>
<p> ✍ Create React App을 통해 React app을 키고 꺼보자
<strong>1. terminal에 <code>npx create-react-app reactexam1</code> 입력</strong>
: &#39;reactexam1&#39;이라는 프로젝트를 만들겠다.
설치 후
<strong>2. terminal에 <code>npm start</code>를 입력 **
localhost 주소로 react 창이 나타남
📌 내 컴퓨터가 서버 역할을 하는 것
**3. terminal에서 ctrl+c 입력</strong>
<code>일괄 작업을 끝내시겠습니까?</code> 에서 y 입력</p>
<blockquote>
<p>파일 : App.js</p>
</blockquote>
<pre><code class="language-javascript">import &#39;./App.css&#39;;
function App() {
  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;header className=&quot;App-header&quot;&gt;
        &lt;h2&gt;hi react&lt;/h2&gt;
      &lt;/header&gt;
    &lt;/div&gt;
  );
}
export default App;</code></pre>
<ul>
<li>JSX : React에서 사용하는 문법. JS의 변수나 함수와 같은 값을 HTML에 쉽게 넣을 수 있음. </li>
</ul>
<blockquote>
<p>파일 : index.js</p>
</blockquote>
<pre><code class="language-javascript">import React from &#39;react&#39;;
import ReactDOM from &#39;react-dom/client&#39;;
import &#39;./index.css&#39;;
import App from &#39;./App&#39;;
&gt;
const root = ReactDOM.createRoot(document.getElementById(&#39;root&#39;));
root.render(
  &lt;React.StrictMode&gt;
    &lt;App /&gt;
  &lt;/React.StrictMode&gt;
);</code></pre>
<p> App.js에서 <code>export default App;</code>으로 내보내기 한 App 컴포넌트는 
 다른 파일에서 <code>import App from &#39;./App&#39;;</code>으로 받아올 수 있음 =&gt; <strong>ES module 시스템</strong></p>
<hr>
<p>📌 JSX 규칙</p>
<ul>
<li>태그를 열었으면 <code>/</code>를 이용해서 꼭 닫아줘야함 </li>
<li>Self-closing tag : <code>&lt;image /&gt;</code> 와 같이 열자마자 닫히는 태그들</li>
<li>컴포넌트를 만들 때 모든 태그들은 가장 바깥에 있는 하나의 최상위 태그로 감싸줘야함<blockquote>
<p><code>React.fragment</code>로 최상위 태그 대체할 수 있음</p>
<pre><code class="language-javascript">function App() {
return (
&lt;React.Fragment&gt;
&lt;MyHeader/&gt;
    &lt;header className=&quot;App-header&quot;&gt;
      &lt;h2&gt;hi react&lt;/h2&gt;
    &lt;/header&gt;
&lt;/React.Fragment&gt;
);
}</code></pre>
</blockquote>
</li>
</ul>
<p>*혹은 <code>&lt;&gt; &lt;/&gt;</code>처럼 빈 태그도 사용 가능</p>
<pre><code></code></pre><hr>
<p>✍ JSX와 CSS를 결합해서 스타일링 해보자</p>
<p><strong>1. css 파일 import</strong></p>
<pre><code class="language-javascript">import &#39;./App.css&#39;;</code></pre>
<p><strong>2. inline styling</strong></p>
<pre><code class="language-javascript">function App() {

  const style = {
    App: {
      backgroundColor: &quot;black&quot;,
      color: &#39;white&#39;,
    },
    h2 : {
      color: &#39;red&#39;,
    },
    bold_text : {
      color: &#39;green&#39;,
    },
  };
// 별도의 css 파일을 사용하지 않고 객체를 만들어서 inline으로 스타일 적용
  return (
&lt;div style={style.App}&gt;
&lt;MyHeader/&gt;
        &lt;h2 style={style.h2}&gt;hi react&lt;/h2&gt;
&lt;b style={style.bold_text} id=&quot;bold_text&quot;&gt;React.js&lt;/b&gt;
&lt;/div&gt;
  );
}
</code></pre>
<hr>
<p>📌 조건부 렌더링</p>
<pre><code class="language-javascript">function App() {
  const number = 5;
  return (
&lt;div&gt;
&lt;MyHeader/&gt;
&lt;h2&gt;hi react&lt;/h2&gt;
&lt;b id=&quot;bold_text&quot;&gt;
{number}는 : {number % 2 === 0? &#39;짝수&#39; : &#39;홀수&#39;}
&lt;/b&gt;
&lt;/div&gt;
  );
}</code></pre>
<ul>
<li>JSX에서 <code>{}</code>안에는 값이나 변수가 들어갈 수 있음(함수 포함)</li>
<li>위와 같이 현재 조건에 따라 렌더링 할 값이 달라질 때 <strong>삼항연산자</strong>는 React에서 사용하는 방법 중 하나</li>
</ul>
<hr>
<h4 id="studying">Studying...</h4>
<blockquote>
</blockquote>
<p><a href="https://inf.run/qAuJ">https://inf.run/qAuJ</a>
한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지 by 이정환 Winterlood</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Node.js]]></title>
            <link>https://velog.io/@dev-sjko/Node.js</link>
            <guid>https://velog.io/@dev-sjko/Node.js</guid>
            <pubDate>Sun, 21 Aug 2022 10:29:08 GMT</pubDate>
            <description><![CDATA[<h2 id="nodejs">Node.js</h2>
<ul>
<li>Javascript는 자바스크립트 엔진이 탑재된 브라우저에서만 사용할 수 있었음</li>
<li><strong>웹 브라우저가 아닌 곳에서 Javascript를 사용하기 위해서 만들어진 프로그램</strong></li>
<li>크롬에서 사용하는 JS 엔진인 v8 엔진을 사용하여 만들어짐(v8은 C++로 개발됨)</li>
<li>자바스크립트의 실행 환경. JavaScript&#39;s runtime이라고도 함.</li>
<li>runtime : 특정 언어로 만든 프로그램들을 실행할 수 있는 환경</li>
<li>Node.js를 사용하면 JS로 웹 서버까지도 개발 가능</li>
<li>React의 바탕</li>
</ul>
<h4 id="terminal">Terminal</h4>
<ul>
<li>Graphic User Interface(GUI) : 아이콘, 커서(마우스), 더블 클릭 등을 이용해서 프로그램을 실행하는 방법</li>
<li>Command Line Interface(CLI) : 명령어를 직접 타이핑해서 명령을 내리는 것</li>
<li>Terminal : CLI 방식으로 프로그램을 실행할 때 명령어를 입력하는 곳
<del>GUI 대신 CLI를 사용하는 이유는..?</del></li>
</ul>
<hr>
<p>파일 : calc.js</p>
<pre><code class="language-javascript">// 계산 기능을 하는 파일

const add = (a, b) =&gt; a + b;
const sub = (a, b) =&gt; a - b;

module.exports = {
    moduleName: &#39;calc module&#39;,
    add: add,
    sub: sub,
};
</code></pre>
<ul>
<li><code>module</code> : 어떤 기능을 담당하는 분리된 파일</li>
<li>calc.js는 계산 기능을 담당하는 모듈</li>
<li>Node.js에서는 <code>module.exports</code>를 사용해서 객체 단위로 모듈을 내보낼 수 있음</li>
</ul>
<p>파일 : index.js</p>
<pre><code class="language-javascript">const calc = require(&#39;./calc&#39;);
console.log(calc);</code></pre>
<ul>
<li><code>require</code> : 경로를 지정해서 모듈을 볼러옴. Node.js의 내장 함수. </li>
</ul>
<blockquote>
<p>terminal에 <code>node index.js</code> 입력
실행 결과</p>
</blockquote>
<pre><code class="language-node">{
 moduleName: &#39;calc module&#39;,
 add: [Function: add],
 sub: [Function: sub]
} </code></pre>
<ul>
<li><code>module.exports</code>와 <code>require</code>는 Node.js의 기능!</li>
<li><strong>commonJS</strong>
Node.js에서 기본적으로 제공되는 시스템
모듈을 내보내고 다른 파일에서 받아서 사용하는 모듈 시스템</li>
</ul>
<hr>
<p>✍ npm으로 node.js package 만들고 외부 모듈을 실행해보자</p>
<h3 id="npmnode-package-manager">npm(Node package manager)</h3>
<ul>
<li>자바스크립트 런타임 환경 Node.js의 기본 패키지 관리 도구</li>
<li>다른 사람이 만든 node.js 모듈을 다운받아서 사용할 수 있도록 도와줌
package : 누군가 만들어놓은 node.js 모듈</li>
</ul>
<p>📌 package 초기 설정
터미널에 <code>npm init</code> 입력
설정 후 생성된 <code>package.json</code> : 프로젝트의 정보를 json으로 기록한 환경설정 파일</p>
<blockquote>
<p>파일: package.json</p>
</blockquote>
<pre><code class="language-json">{
  &quot;name&quot;: &quot;package-example1&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;scripts&quot;: {
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;,
  },
  &quot;author&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;,
  &quot;dependencies&quot;: {
    &quot;randomcolor&quot;: &quot;^0.6.2&quot;
  }
}</code></pre>
<ul>
<li><code>main</code>: 패키지를 실행할 때 제일 먼저 실행할 파일</li>
<li><code>scripts</code>: 자주 사용하는 명령을 간단하게 실행시킬 수 있는 명령어들
terminal에 <code>npm 키워드</code> 를 입력하면 package.json 내의 script에서 해당 명령어에 해당하는 문자열이 terminal에 입력됨 -&gt; 명령 실행</li>
</ul>
<hr>
<p><strong>✍ npmjs.com에서 원하는 모듈 다운받아 보기</strong>
terminal에 <code>npm i randomcolor</code> 입력
*package.json이 있는 모듈에서 설치해야 함</p>
<p>📌 package.json에 <code>&quot;dependencies&quot;</code>가 추가되며 어떤 package를 설치했는지, 해당 버전의 범위를 기록함</p>
<pre><code class="language-json">  &quot;dependencies&quot;: {
    &quot;randomcolor&quot;: &quot;^0.6.2&quot;
    }</code></pre>
<p>📌 <code>node_modules</code> 폴더에 실제 package의 코드, 파일들이 들어옴
📌 <code>package-lock.json</code>에 나의 package에 설치된 외부 package들의 실제 버전이 기록되어있음</p>
<blockquote>
<p>commonJS로 require를 이용해서 모듈을 가져올 때</p>
</blockquote>
<pre><code class="language-javascript">const calc = require(&#39;./calc&#39;);</code></pre>
<p>경로 없이 package 이름을 적어주면 node_modules에 있는 모듈을 가져옴</p>
<pre><code class="language-javascript">const randomColor = require(&#39;randomcolor&#39;);</code></pre>
]]></description>
        </item>
    </channel>
</rss>