<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>s00jin 님의 블로그</title>
    <link>https://s00jin.tistory.com/</link>
    <description>s00jin 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Mon, 6 Apr 2026 09:38:13 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>s00jin</managingEditor>
    <image>
      <title>s00jin 님의 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/7903767/attach/8a3e7edca53d447894468519c4b5f896</url>
      <link>https://s00jin.tistory.com</link>
    </image>
    <item>
      <title>[최종 코딩테스트 회고] 8기 프리코스 종료 - !</title>
      <link>https://s00jin.tistory.com/51</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;프리코스 1차 합격&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-13 오후 4.15.37.png&quot; data-origin-width=&quot;2228&quot; data-origin-height=&quot;988&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1nrHA/dJMcaaYkdRZ/gRYJ9dyVYQ8u4gChNgPSa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1nrHA/dJMcaaYkdRZ/gRYJ9dyVYQ8u4gChNgPSa1/img.png&quot; data-alt=&quot;감격의 1차 합격 메일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1nrHA/dJMcaaYkdRZ/gRYJ9dyVYQ8u4gChNgPSa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1nrHA%2FdJMcaaYkdRZ%2FgRYJ9dyVYQ8u4gChNgPSa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2228&quot; height=&quot;988&quot; data-filename=&quot;스크린샷 2026-01-13 오후 4.15.37.png&quot; data-origin-width=&quot;2228&quot; data-origin-height=&quot;988&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;감격의 1차 합격 메일&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;떨어질 줄 알았던 프리코스에 1차 합격을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프리코스가 끝난 이후 번아웃이 와서 한동안 공부를 거의 하지 않았다. 그러다 보니 코딩 실력이 바닥을 찍은 상태였는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1차 합격 소식을 듣고 정신을 붙잡고 다시 공부를 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 코테 후기에 기출을 많이 풀라는 이야기가 있어, 기출을 많이 풀었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기출을 5시간 타이머를 맞춰 풀어봤지만, 시간 내에 풀리지 않는 문제들이 많아 스스로에 대한 자신감이 많이 떨어졌었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최종 코딩테스트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 최종 코딩테스트는 이전 최종 코테들과는 조금 다른 방식이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시험 시간은 &lt;b&gt;코딩 테스트 4시간 + 소감문 작성 1시간&lt;/b&gt;으로 구성되어 있었고, 코테 문제는 프리코스 3주차 로또 미션이 변형된 형태였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 기능 구현 외에도 &lt;b&gt;도전 과제&lt;/b&gt;로 새로운 기능을 추가하거나 리팩토링을 수행하는 선택지가 주어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 새로운 기능을 추가하는 방향을 고민했지만, 로또 기본 기능 구현에 예상보다 많은 시간을 사용하게 되면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도전 과제는 리팩토링으로 선택하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 코테에서는 InputView와 OutputView가 제공되었고, 이 클래스들은 수정이 불가능했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 OutputView에서 로또들을 출력하는 메서드의 파라미터 타입이 List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt;였다. 이 때문에 처음에는 Lotto 도메인을 따로 만들지 않고, Lottos에서 관리하던 List&amp;lt;Integer&amp;gt; 형태의 로또를 그대로 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 테스트에서 오류가 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류를 확인해 보니 sort 과정에서 문제가 발생한 것처럼 보였지만, 처음에는 정확한 원인을 알지 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글링을 하며 원인을 찾아본 결과, List&amp;lt;Integer&amp;gt; 자체를 기준으로 정렬하려고 했던 구조가 문제였다는 걸 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 Lotto 도메인을 새로 만들고, 로또 번호 정렬 책임을 Lotto 내부로 옮겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 OutputView에서는 List&amp;lt;Integer&amp;gt; 형태로 꺼내서 전달하도록 구현했더니 테스트를 통과할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로또 기능 자체를 구현하는 데에는 약 1시간 30분 정도가 걸렸지만, 이 구조적인 오류를 해결하는 데에만 거의 2시간을 사용했다. 그 결과 새로운 기능을 추가할 시간은 부족했고, 남은 시간 동안은 프리코스 기간 동안 받았던 피드백을 떠올리며 리팩토링을 진행한 후 과제를 제출했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-13 오후 4.07.01.png&quot; data-origin-width=&quot;1576&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFJfBX/dJMb996bobb/Mt8y9owVKCWjh8rD5MbgLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFJfBX/dJMb996bobb/Mt8y9owVKCWjh8rD5MbgLK/img.png&quot; data-alt=&quot;그래도 테스트는 전부 통과했다!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFJfBX/dJMb996bobb/Mt8y9owVKCWjh8rD5MbgLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFJfBX%2FdJMb996bobb%2FMt8y9owVKCWjh8rD5MbgLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1576&quot; height=&quot;474&quot; data-filename=&quot;스크린샷 2026-01-13 오후 4.07.01.png&quot; data-origin-width=&quot;1576&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그래도 테스트는 전부 통과했다!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소감문 작성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 소감문 작성 시간이 별도로 주어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전과 같은 방식일 거라고 생각해서 미리 소감문을 작성해 두었고, 이 시간에는 내용을 조금 더 보완해 추가로 작성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데&amp;hellip; 소감문에 &lt;b&gt;정해진 형식이 있다는 걸 시험이 끝난 후에 알게 되었다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 최종 코테 회고 글들을 보다가 그제서야 형식이 있다는 사실을 알게 되어 정말 당황했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시험을 볼 때 미션을 처음 읽고, &amp;ldquo;기본 기능 구현 후 도전 과제를 수행하라&amp;rdquo;는 안내를 보고 기본 기능 구현 부분만 먼저 집중해서 읽었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 기능을 다 구현한 뒤에 도전 과제 설명을 다시 읽었는데, 바보같이 그 아래에 소감문 형식에 대한 안내가 있는 줄은 전혀 몰라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크롤을 더 내리지 않았다... 결국 그 부분을 끝까지 읽지 않은 채 자유형식의 소감문을 제출해버렸다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;액댐&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 제주도에 살고 있어서 1차 합격 메일을 받자마자 비행기표를 끊었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;1174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2morV/dJMcabQsfgg/DvPVuk5ZIvESDoJX5WkD1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2morV/dJMcabQsfgg/DvPVuk5ZIvESDoJX5WkD1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2morV/dJMcabQsfgg/DvPVuk5ZIvESDoJX5WkD1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2morV%2FdJMcabQsfgg%2FDvPVuk5ZIvESDoJX5WkD1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;322&quot; height=&quot;435&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;1174&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 시험 전날, 올라가는 항공편이 결항되었다는 문자를 받았다. 급하게 같은 시간대의 다른 항공편을 예매했는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;편도 가격이 무려 10만 원이었다&amp;hellip;^^&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 그날 저녁에는 강풍 주의보까지 떴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히 시험 당일에 타야 했던 비행기는 결항되지는 않았지만, 지연되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강풍 때문에 비행기가 정말 많이 흔들렸고, 살면서 처음으로 비행기 멀미를 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지연을 어느 정도 예상하고 비행기표를 끊어둔 덕분에 시험장에는 시간 내에 도착해 무사히 시험을 볼 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시험장은 지정석이었고, 나는 강의실 뒤 회의실 안에 배정되었다. 여기서도 작은 문제가 있었다. 코치님들이 시험 관련 안내를 강의실 앞에서 해주셨는데, 마이크 같은 장비 없이 안내를 진행하셔서 회의실 안쪽에서는 정말 거의 들리지 않았다. 그래서 분위기를 보며 눈치껏 행동했다..ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시험이 끝나자마자 부모님께 끝났다고 연락을 드렸는데, 바로 전화가 왔다. 밤 비행기들이 결항되고 있다는 소식이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예매해 둔 비행기는 결항되지 않아 공항으로 이동했지만, 공항에 도착해 보니 결항과 지연으로 수속장에 사람들이 가득 차 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 내 비행기도 지연되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 지연되어 늦게 출발한 이후였다. 제주도 상공까지 왔지만, 강풍으로 인해 착륙 시도를 하지 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;착륙 직전 관제탑에서 착륙 불가 판정이 내려졌고, 비행기는 땅에 닿기 직전에 급상승했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이후 제주도 상공을 한참 동안 선회하다가 다시 착륙을 시도했다. 그 과정에서 비행기는 정말 심하게 흔들렸고, 주변 사람들은 하나둘씩 이어폰을 빼고 휴대폰을 덮은 채 창밖만 바라보고 있었다. 몇몇 분들은 비행기 모드를 끄고 연락을 돌리기도 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 출발한 지 약 2시간 만에 제주도에 착륙할 수 있었다. (공항 제일 마지막 손님으로 착륙했다!! 그 뒤에 비행기는 다 결항되었다)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리하며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해 해커톤을 진행하며 멘토님을 통해 우테코의 존재를 알게되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우테코의 진행방식이 너무 매력적이였으며, 공부에 진심인 사람들이 모여 10 달 간 같이 공부한다는 것이 너무 재미있을 것 같았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이 사람들 사이에서 같이 시너지를 내며 성장하고 싶은 마음이 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 취준과 인턴 준비를 다 미루고 우테코에 투자했다. 내 생각대로 프리코스만 했을 뿐인데도 배운 내용이 너무 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발적으로도 정신적으로 많이 성장한 시간을 보낸것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 소중한 기회를 지원자 모두에게 준 우테코에게 정말 감사하다. 최종 합격을 할 수 있을지는 모르겠지만 (물론 너무 하고 싶다..ㅎㅎ)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 값진 경험을 얻은 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종합격 결과에 상관없이 이 과정만으로도 개발 인생의 터닝포인트가 될 것 같다.&lt;/p&gt;</description>
      <category>우아한테크코스</category>
      <author>s00jin</author>
      <guid isPermaLink="true">https://s00jin.tistory.com/51</guid>
      <comments>https://s00jin.tistory.com/51#entry51comment</comments>
      <pubDate>Tue, 13 Jan 2026 16:25:10 +0900</pubDate>
    </item>
    <item>
      <title>[오픈미션] 리액트 공부 3</title>
      <link>https://s00jin.tistory.com/50</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;서비스를 구현하면서 추가 공부한 내용입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;useState&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서 &amp;ldquo;변하는 값&amp;rdquo;을 관리하는 기능.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const [timeLeft, setTimeLeft] = useState(7000);

&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;timeLeft = 현재 값&lt;/li&gt;
&lt;li&gt;setTimeLeft = 값을 변경하는 함수&lt;/li&gt;
&lt;li&gt;setState가 호출되면 &lt;b&gt;컴포넌트가 다시 렌더링됨&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;useRef&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더링과 상관없이 &lt;b&gt;값을 유지&lt;/b&gt;하는 저장소.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;값 변경해도 렌더링되지 않음&lt;/li&gt;
&lt;li&gt;렌더링해도 값이 유지됨&lt;/li&gt;
&lt;li&gt;보통 &lt;b&gt;setInterval ID, DOM 요소, 이전 값 저장&lt;/b&gt; 등에 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;const timeRef = useRef(null);

&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;useEffect&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;특정 상황에서 실행되는 코드&amp;rdquo;를 넣는 공간.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;useEffect(() =&amp;gt; {
    console.log(&quot;마운트된 후 실행&quot;);
}, []);

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;의존성 배열(deps)에 따라 다음이 결정됨&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;deps&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;실행 시기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[]&lt;/td&gt;
&lt;td&gt;처음 1번만 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[state]&lt;/td&gt;
&lt;td&gt;state 값이 바뀔 때마다 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;td&gt;모든 렌더링 이후 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;useEffect(() =&amp;gt; {
    // interval 생성
}, []);

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; &lt;b&gt;한 번만&lt;/b&gt; 실행됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; interval이 중복 생성되는 문제를 완전히 제거함&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;useCallback&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 메모이징하는 훅.&lt;/p&gt;
&lt;pre class=&quot;nimrod&quot;&gt;&lt;code&gt;const capture = useCallback(() =&amp;gt; {...}, [webcamRef]);

&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;deps 안의 값이 바뀌지 않으면 &amp;lsquo;같은 함수&amp;rsquo;로 취급됨&lt;/li&gt;
&lt;li&gt;렌더링 때마다 함수가 새로 만들어지는걸 막아줌&lt;/li&gt;
&lt;li&gt;자식 컴포넌트에 props로 넘길 때 성능 최적화됨&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;setInterval과 clearInterval&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;setInterval&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;setInterval(() =&amp;gt; {
    console.log(&quot;1초마다 실행&quot;);
}, 1000);

&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일정 시간이 지날 때마다 함수를 반복 실행&lt;/li&gt;
&lt;li&gt;리턴값은 interval ID (숫자)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;clearInterval&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;clearInterval(intervalID);

&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;setInterval로 만든 반복 타이머를 종료&lt;/li&gt;
&lt;li&gt;정확한 &amp;ldquo;interval ID&amp;rdquo;가 필요함&lt;/li&gt;
&lt;li&gt;&amp;rarr; 그래서 useRef로 그 ID를 저장해두는 것&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>우아한테크코스</category>
      <author>s00jin</author>
      <guid isPermaLink="true">https://s00jin.tistory.com/50</guid>
      <comments>https://s00jin.tistory.com/50#entry50comment</comments>
      <pubDate>Mon, 24 Nov 2025 18:30:28 +0900</pubDate>
    </item>
    <item>
      <title>[오픈미션] 리액트 공부 2</title>
      <link>https://s00jin.tistory.com/49</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;컴포넌트, JSX&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트는 페이지 단위가 아닌 컴포넌트 단위로 나눠서 조합해서 구현함&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import './App.css';

function App() {
  const name = &quot;Soojin&quot;;
  const naver = {
    name: &quot;네이버&quot;,
    url: &quot;https://www.naver.com&quot;
  }
  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;h1 
        style={{
          color: &quot;yellow&quot;,
          backgroundColor: &quot;black&quot;,
        }}&amp;gt;Welcome, {name}
        &amp;lt;/h1&amp;gt;
        &amp;lt;a href={naver.url}&amp;gt;{naver.name}&amp;lt;/a&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;컴포넌트 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;component 폴더 만들고 그 안에 넣음&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const Hello = () =&amp;gt; {
    return &amp;lt;p&amp;gt;Hello&amp;lt;/p&amp;gt;
}

export default Hello;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;export default function Hello() {
    return &amp;lt;p&amp;gt;Hello&amp;lt;/p&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 아래 코드가 같은 것&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import './App.css';
import Hello from './component/Hello';

function App() {

  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;Hello /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하는 곳에 임포트 해줘야함&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;import Hello from './component/Hello';&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할때는 그냥 태그로 써주면 됨&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;Hello /&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Jsx는 하나의 태그로만 구성됨&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import World from &quot;./World&quot;

export default function Hello() {
    return (
    &amp;lt;div&amp;gt;
        &amp;lt;h1&amp;gt;Hello&amp;lt;/h1&amp;gt;
        &amp;lt;World /&amp;gt;
    &amp;lt;/div&amp;gt;
    );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 작성하면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;블로그1.png&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;966&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o3RqI/dJMcaiaGgBk/gJk8wCUk2u7xloXPiainfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o3RqI/dJMcaiaGgBk/gJk8wCUk2u7xloXPiainfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o3RqI/dJMcaiaGgBk/gJk8wCUk2u7xloXPiainfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo3RqI%2FdJMcaiaGgBk%2FgJk8wCUk2u7xloXPiainfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1504&quot; height=&quot;966&quot; data-filename=&quot;블로그1.png&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;966&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 잘 표현됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 div 태그를 제거하면&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import World from &quot;./World&quot;

export default function Hello() {
    return (
    &amp;lt;div&amp;gt;
        &amp;lt;h1&amp;gt;Hello&amp;lt;/h1&amp;gt;
        &amp;lt;World /&amp;gt;
    &amp;lt;/div&amp;gt;
    );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;블로그2.png&quot; data-origin-width=&quot;1502&quot; data-origin-height=&quot;964&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lgy8A/dJMcahpjrx8/DArbJBCJkocDb0BTq3GERk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lgy8A/dJMcahpjrx8/DArbJBCJkocDb0BTq3GERk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lgy8A/dJMcahpjrx8/DArbJBCJkocDb0BTq3GERk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flgy8A%2FdJMcahpjrx8%2FDArbJBCJkocDb0BTq3GERk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1502&quot; height=&quot;964&quot; data-filename=&quot;블로그2.png&quot; data-origin-width=&quot;1502&quot; data-origin-height=&quot;964&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 오류남&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ div가 아닌 빈 태그로 감싸줘도 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CSS 작성법&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 인라인 태그로 작성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체로 작성해야함 &amp;rArr; 중괄호 안에 중괄호 하나 더 해서 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import World from &quot;./World&quot;

export default function Hello() {
    return (
    &amp;lt;div&amp;gt;
        &amp;lt;h1 style={
            {
                color : &quot;blue&quot;,
                backgroundColor: &quot;yellow&quot;
            }
        }&amp;gt;Hello&amp;lt;/h1&amp;gt;
        &amp;lt;World /&amp;gt;
    &amp;lt;/div&amp;gt;
    );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 말고 카멜 케이스로 작성&lt;br /&gt;숫자는 그냥 기입해도 되는데, px 붙는거는 따옴표로 감싸줘야 함&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;블로그3.png&quot; data-origin-width=&quot;1470&quot; data-origin-height=&quot;778&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cne2Dg/dJMcahpjrBg/f2e2v2aMCkUEDaFgJdBwW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cne2Dg/dJMcahpjrBg/f2e2v2aMCkUEDaFgJdBwW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cne2Dg/dJMcahpjrBg/f2e2v2aMCkUEDaFgJdBwW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcne2Dg%2FdJMcahpjrBg%2Ff2e2v2aMCkUEDaFgJdBwW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1470&quot; height=&quot;778&quot; data-filename=&quot;블로그3.png&quot; data-origin-width=&quot;1470&quot; data-origin-height=&quot;778&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;2. 일반 CSS 파일&lt;/h2&gt;
&lt;pre id=&quot;code_1763975525234&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;
import './App.css';
import Hello from './component/Hello';
import Welcome from './component/Welcome';

function App() {

  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;Hello /&amp;gt;
      &amp;lt;Welcome /&amp;gt;
      &amp;lt;div className='box'&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;App.js&lt;/p&gt;
&lt;pre id=&quot;code_1763975541099&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;.box {
  width: 100px;
  height: 100px;
  background-color: lightblue;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;App.css&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;블로그4.png&quot; data-origin-width=&quot;1506&quot; data-origin-height=&quot;872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G0pNP/dJMb99SlDgm/15YCWmljTmuMPO8TpRvJ2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G0pNP/dJMb99SlDgm/15YCWmljTmuMPO8TpRvJ2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G0pNP/dJMb99SlDgm/15YCWmljTmuMPO8TpRvJ2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG0pNP%2FdJMb99SlDgm%2F15YCWmljTmuMPO8TpRvJ2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1506&quot; height=&quot;872&quot; data-filename=&quot;블로그4.png&quot; data-origin-width=&quot;1506&quot; data-origin-height=&quot;872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;3. CSS Module 파일&lt;/h2&gt;
&lt;pre id=&quot;code_1763975688531&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.box {
  width: 200px;
  height: 100px;
  background-color: rebeccapurple;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hello.module.css&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763975709590&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import styles from &quot;./Hello.module.css&quot;;

&amp;lt;div className={styles.box}&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hello.js&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴포넌트별로 스타일이 &lt;b&gt;자동 네임스페이스 처리&lt;/b&gt;됨&lt;/li&gt;
&lt;li&gt;클래스 충돌 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-end=&quot;2207&quot; data-start=&quot;2195&quot;&gt;이벤트 처리&lt;/h1&gt;
&lt;h2 data-end=&quot;2226&quot; data-start=&quot;2209&quot; data-ke-size=&quot;size26&quot;&gt;1. 먼저 함수를 만들어 놓고 사용&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1763975826740&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;
export default function Hello() {
    function showName() {
        console.log(&quot;Mike&quot;);
    }
    return (
    &amp;lt;div&amp;gt;
        &amp;lt;h1&amp;gt;Hello&amp;lt;/h1&amp;gt;
        &amp;lt;button onClick={showName}&amp;gt;Show name&amp;lt;/button&amp;gt;
        &amp;lt;button&amp;gt;Show age&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-end=&quot;2348&quot; data-start=&quot;2334&quot; data-ke-size=&quot;size26&quot;&gt;2. 내부에 직접 함수 호출&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1763975873974&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function Hello() {
    function showName() {
        console.log(&quot;Mike&quot;);
    }
    return (
    &amp;lt;div&amp;gt;
        &amp;lt;h1&amp;gt;Hello&amp;lt;/h1&amp;gt;
        &amp;lt;button onClick={showName}&amp;gt;Show name&amp;lt;/button&amp;gt;
        &amp;lt;button onClick={() =&amp;gt; {
            console.log(30);
        }}&amp;gt;Show age&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;이 방식의 장점은 매개변수를 전달하기 쉬움&lt;/div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1763975927713&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function Hello() {
    function showName() {
        console.log(&quot;Mike&quot;);
    }
    function showAge(age) {
        console.log(age);
    }
    return (
    &amp;lt;div&amp;gt;
        &amp;lt;h1&amp;gt;Hello&amp;lt;/h1&amp;gt;
        &amp;lt;button onClick={showName}&amp;gt;Show name&amp;lt;/button&amp;gt;
        &amp;lt;button onClick={() =&amp;gt; {
            showAge(30);
        }}&amp;gt;Show age&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-end=&quot;2581&quot; data-start=&quot;2556&quot; data-ke-size=&quot;size26&quot;&gt;State&amp;nbsp;&lt;/h2&gt;
&lt;pre id=&quot;code_1763975984535&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useState } from &quot;react&quot;;

export default function Hello() {
  const [name, setName] = useState(&quot;Mike&quot;);

  function changeName() {
    setName(name === &quot;Mike&quot; ? &quot;Jane&quot; : &quot;Mike&quot;);
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h2&amp;gt;{name}&amp;lt;/h2&amp;gt;
      &amp;lt;button onClick={changeName}&amp;gt;changeName&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;2978&quot; data-start=&quot;2904&quot; data-ke-size=&quot;size16&quot;&gt;state를 바꾸면 자동으로 화면도 다시 렌더링됨&lt;br /&gt;state를 직접 수정해도 렌더링 X &amp;rarr; 반드시 setState 사용&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-start=&quot;2556&quot; data-end=&quot;2581&quot;&gt;라우터 구현&lt;/h2&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;npm install react-router-dom 
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터 설치&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;import { BrowserRouter, Route, Switch } from &quot;react-router-dom&quot;;

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;App.js에 불러오기&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;import DayList from &quot;./component/DayList&quot;;
import Header from &quot;./component/Header&quot;;
import Day from &quot;./component/Day&quot;;
import { BrowserRouter, Route, Routes } from &quot;react-router-dom&quot;;

function App() {
  return (
    &amp;lt;BrowserRouter&amp;gt;
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;Header /&amp;gt;
      &amp;lt;Routes&amp;gt;
        &amp;lt;Route path=&quot;/&quot; element={&amp;lt;DayList /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;/day&quot; element={&amp;lt;Day /&amp;gt;} /&amp;gt;
      &amp;lt;/Routes&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;/BrowserRouter&amp;gt;
  );
}

export default App;

&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Link를 써야함 href 대신&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { Link } from 'react-router-dom';
import dummy from '../db/data.json';

export default function DayList() {
    console.log(dummy);
    return &amp;lt;ul className='list_day'&amp;gt;
        {dummy.days.map(day =&amp;gt; (
            &amp;lt;li key={day.id}&amp;gt;
                &amp;lt;Link to=&quot;/day&quot;&amp;gt;Day {day.day}&amp;lt;/Link&amp;gt;
            &amp;lt;/li&amp;gt;
        ))}
    &amp;lt;/ul&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다이나믹 라우트 이용할때는 콜론 이용 (:)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;import DayList from &quot;./component/DayList&quot;;
import Header from &quot;./component/Header&quot;;
import Day from &quot;./component/Day&quot;;
import { BrowserRouter, Route, Routes } from &quot;react-router-dom&quot;;
import EmptyPage from &quot;./component/EmptyPage&quot;;

function App() {
  return (
    &amp;lt;BrowserRouter&amp;gt;
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;Header /&amp;gt;
      &amp;lt;Routes&amp;gt;
        &amp;lt;Route path=&quot;/&quot; element={&amp;lt;DayList /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;/day/:day&quot; element={&amp;lt;Day /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;*&quot; element={&amp;lt;EmptyPage /&amp;gt;} /&amp;gt;
      &amp;lt;/Routes&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;/BrowserRouter&amp;gt;
  );
}

export default App;

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 day라는 변수로 값을 받을 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 day로 보내진 값은 useParams를 사용해서 이용&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import dummy from &quot;../db/data.json&quot;;
import { useParams } from &quot;react-router-dom&quot;;

export default function Day() {
    const a = useParams();
    const day = Number(a.day);

    const wordList = dummy.words.filter(word =&amp;gt; (
        word.day === day
    ));

    return &amp;lt;&amp;gt;
    &amp;lt;h2&amp;gt;Day {day}&amp;lt;/h2&amp;gt;
    &amp;lt;table&amp;gt;
        &amp;lt;tbody&amp;gt;
            {wordList.map(word =&amp;gt; (
                &amp;lt;tr&amp;gt;
                    &amp;lt;td&amp;gt;
                        {word.eng}
                    &amp;lt;/td&amp;gt;
                    &amp;lt;td&amp;gt;
                        {word.kor}
                    &amp;lt;/td&amp;gt;
                &amp;lt;/tr&amp;gt;
            ))}
        &amp;lt;/tbody&amp;gt;
    &amp;lt;/table&amp;gt;
    &amp;lt;/&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;json-server 사용하기&lt;/h2&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;npm install -g json-server
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;json-server --watch ./src/db/data.json --port 3001

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 강의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://www.youtube.com/playlist?list=PLZKTXPmaJk8J_fHAzPLH8CJ_HO_M33e7-&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/playlist?list=PLZKTXPmaJk8J_fHAzPLH8CJ_HO_M33e7-&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1763976207412&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;React js 강좌&quot; data-og-description=&quot; &quot; data-og-host=&quot;www.youtube.com&quot; data-og-source-url=&quot;https://www.youtube.com/playlist?list=PLZKTXPmaJk8J_fHAzPLH8CJ_HO_M33e7-&quot; data-og-url=&quot;http://www.youtube.com/playlist?list=PLZKTXPmaJk8J_fHAzPLH8CJ_HO_M33e7-&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bvJalV/hyZOrh76iF/KpELDgSobDoWi9oHcZpptk/img.jpg?width=480&amp;amp;height=270&amp;amp;face=0_0_480_270,https://scrap.kakaocdn.net/dn/cCiCe6/hyZN4hLdHX/ETauRVdwG5K0IBJs5Qm1V1/img.jpg?width=480&amp;amp;height=270&amp;amp;face=0_0_480_270&quot;&gt;&lt;a href=&quot;https://www.youtube.com/playlist?list=PLZKTXPmaJk8J_fHAzPLH8CJ_HO_M33e7-&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.youtube.com/playlist?list=PLZKTXPmaJk8J_fHAzPLH8CJ_HO_M33e7-&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bvJalV/hyZOrh76iF/KpELDgSobDoWi9oHcZpptk/img.jpg?width=480&amp;amp;height=270&amp;amp;face=0_0_480_270,https://scrap.kakaocdn.net/dn/cCiCe6/hyZN4hLdHX/ETauRVdwG5K0IBJs5Qm1V1/img.jpg?width=480&amp;amp;height=270&amp;amp;face=0_0_480_270');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;React js 강좌&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.youtube.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>우아한테크코스</category>
      <author>s00jin</author>
      <guid isPermaLink="true">https://s00jin.tistory.com/49</guid>
      <comments>https://s00jin.tistory.com/49#entry49comment</comments>
      <pubDate>Mon, 24 Nov 2025 18:24:34 +0900</pubDate>
    </item>
    <item>
      <title>[오픈미션] 리액트 공부 1</title>
      <link>https://s00jin.tistory.com/48</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 기초 강의를 정리한 내용입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트는&amp;nbsp; 페이지 전체를 한 번에 만드는 방식이 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트 단위로 조립해서 사용하는 방식&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리액트 작업 환경 만들기&lt;/h3&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;npx create-react-app my-app
// my-app 부분은 작업할 디렉토리

npx create-react-app .
// .은 현재 디렉토리를 의미
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개발 시 서버 실행&lt;/h3&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;npm start
// 리액트 실행
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src/index.js 파일이 실행시 처음 실행되는 파일 (메인 파일 느낌?)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배포시 빌드&lt;/h3&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하면 build 폴더 생김&lt;/p&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;npx serve -s build // -s 옵션은 build 폴더의 index.js로 시작하게 해주는 옵션
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어 실행하면 index.js로 시작하는 서버 배포됨&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;컴포넌트 만들기&lt;/h2&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import logo from './logo.svg';
import './App.css';

function App() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;header&amp;gt;
        &amp;lt;h1&amp;gt;&amp;lt;a href=&quot;/&quot;&amp;gt;WEB&amp;lt;/a&amp;gt;&amp;lt;/h1&amp;gt;
      &amp;lt;/header&amp;gt;
      &amp;lt;nav&amp;gt;
        &amp;lt;ol&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;a href='/read/1'&amp;gt;html&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;a href='/read/2'&amp;gt;css&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;a href='/read/3'&amp;gt;js&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;/ol&amp;gt;
      &amp;lt;/nav&amp;gt;
      &amp;lt;article&amp;gt;
        &amp;lt;h2&amp;gt;WELCOME&amp;lt;/h2&amp;gt;
        Hello, WEB!
      &amp;lt;/article&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 위 코드에서 header, nav, article이 각각 1억줄씩 있다고 생각하면 너무!~~ 어지러움&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리정돈을 해야함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리정돈은 서로 연관된 것들을 모음 &amp;rarr; 거기다 이름을 붙임&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용자 정의 태그 정의하기 = 컴포넌트 (컴퍼넌트)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앞글자는 무조건 대문자&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import logo from './logo.svg';
import './App.css';
function Header() {
  return &amp;lt;header&amp;gt;
        &amp;lt;h1&amp;gt;&amp;lt;a href=&quot;/&quot;&amp;gt;React&amp;lt;/a&amp;gt;&amp;lt;/h1&amp;gt;
      &amp;lt;/header&amp;gt;
}
function Nav() {
  return &amp;lt;nav&amp;gt;
        &amp;lt;ol&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;a href='/read/1'&amp;gt;html&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;a href='/read/2'&amp;gt;css&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;a href='/read/3'&amp;gt;js&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;/ol&amp;gt;
      &amp;lt;/nav&amp;gt;
}
function Article() {
  return &amp;lt;article&amp;gt;
        &amp;lt;h2&amp;gt;WELCOME&amp;lt;/h2&amp;gt;
        Hello, WEB!
      &amp;lt;/article&amp;gt;
}
function App() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Header&amp;gt;&amp;lt;/Header&amp;gt;   
      &amp;lt;Nav&amp;gt;&amp;lt;/Nav&amp;gt;
      &amp;lt;Article&amp;gt;&amp;lt;/Article&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Header() 만든게 컴퍼넌트&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;props (데이터 전달)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모 -&amp;gt; 자식 컴포넌트로 값 전달할 수 있음&lt;/p&gt;
&lt;pre id=&quot;code_1763973680718&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Header title=&quot;React&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트에서는&lt;/p&gt;
&lt;pre id=&quot;code_1763973700954&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Header(props) {
  return &amp;lt;h1&amp;gt;{props.title}&amp;lt;/h1&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre&gt;&lt;code&gt;import logo from './logo.svg';
import './App.css';
function Header(props) { // props 말고 다른 이름이 와도 됨. 하지만 다들 props라고 함
  console.log('props', props, props.title);
  return &amp;lt;header&amp;gt;
        &amp;lt;h1&amp;gt;&amp;lt;a href=&quot;/&quot;&amp;gt;{props.title}&amp;lt;/a&amp;gt;&amp;lt;/h1&amp;gt; // 리턴에 있는 html에서는 {} 중괄호로 감싸줘야 문자열이 아니라 표현식으로 취급
      &amp;lt;/header&amp;gt;
}
function Nav() {
  return &amp;lt;nav&amp;gt;
        &amp;lt;ol&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;a href='/read/1'&amp;gt;html&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;a href='/read/2'&amp;gt;css&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;a href='/read/3'&amp;gt;js&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;/ol&amp;gt;
      &amp;lt;/nav&amp;gt;
}
function Article(props) {
  return &amp;lt;article&amp;gt;
        &amp;lt;h2&amp;gt;{props.title}&amp;lt;/h2&amp;gt;
        {props.body}
      &amp;lt;/article&amp;gt;
}
function App() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Header title=&quot;React&quot;&amp;gt;&amp;lt;/Header&amp;gt;   
      &amp;lt;Nav&amp;gt;&amp;lt;/Nav&amp;gt;
      &amp;lt;Article title=&quot;Welcome&quot; body=&quot;Hello, Web!&quot;&amp;gt;&amp;lt;/Article&amp;gt;
      &amp;lt;Article title=&quot;Hi&quot; body=&quot;React!&quot;&amp;gt;&amp;lt;/Article&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;

&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;li의 값이 하드코딩이라 별로임&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import logo from './logo.svg';
import './App.css';
function Header(props) {
  console.log('props', props, props.title);
  return &amp;lt;header&amp;gt;
        &amp;lt;h1&amp;gt;&amp;lt;a href=&quot;/&quot;&amp;gt;{props.title}&amp;lt;/a&amp;gt;&amp;lt;/h1&amp;gt;
      &amp;lt;/header&amp;gt;
}
function Nav(props) {
  const lis = []
  for(let i=0; i&amp;lt;props.topics.length; i++) {
    let t = props.topics[i];
    lis.push(&amp;lt;li key={t.id}&amp;gt;&amp;lt;a href={'/read/'+t.id}&amp;gt;{t.title}&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;);
  }
  return &amp;lt;nav&amp;gt;
        &amp;lt;ol&amp;gt;
          {lis}
        &amp;lt;/ol&amp;gt;
      &amp;lt;/nav&amp;gt;
}
function Article(props) {
  return &amp;lt;article&amp;gt;
        &amp;lt;h2&amp;gt;{props.title}&amp;lt;/h2&amp;gt;
        {props.body}
      &amp;lt;/article&amp;gt;
}
function App() {
  const topics = [
    {id:1, title:'html', body:'html is ...'},
    {id:2, title:'css', body:'css is ...'},
    {id:3, title:'js', body:'js is ...'}
  ]
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Header title=&quot;React&quot;&amp;gt;&amp;lt;/Header&amp;gt;   
      &amp;lt;Nav topics={topics}&amp;gt;&amp;lt;/Nav&amp;gt;
      &amp;lt;Article title=&quot;Welcome&quot; body=&quot;Hello, Web!&quot;&amp;gt;&amp;lt;/Article&amp;gt;
      &amp;lt;Article title=&quot;Hi&quot; body=&quot;React!&quot;&amp;gt;&amp;lt;/Article&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;

&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;위에서 처럼 배열 lis를 만든 후 {lis} 해주면 리액트에서 자동으로 안에 요소들 해줌&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;배열 요소들을 하나씩 꺼내서 배치해줌&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이벤트&lt;/h2&gt;
&lt;pre id=&quot;code_1763973870990&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;onClick={(event) =&amp;gt; {
  event.preventDefault();
  props.onChangeMode();
}}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1875&quot; data-start=&quot;1836&quot;&gt;event.preventDefault() : 기본 동작 막음&lt;/li&gt;
&lt;li data-end=&quot;1917&quot; data-start=&quot;1876&quot;&gt;props.onChangeMode() : 부모에서 내려준 함수 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트에서 특정 값을 전달하고 싶으면&lt;/p&gt;
&lt;pre id=&quot;code_1763973943578&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;props.onChangeMode(event.target.id)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;event.target은 이벤트 발생한 DOM 요소&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre&gt;&lt;code&gt;import logo from './logo.svg';
import './App.css';
function Header(props) {
  console.log('props', props, props.title);
  return &amp;lt;header&amp;gt;
        &amp;lt;h1&amp;gt;&amp;lt;a href=&quot;/&quot; onClick={function(event) {
          event.preventDefault();
          props.onChangeMode();
        }}&amp;gt;{props.title}&amp;lt;/a&amp;gt;&amp;lt;/h1&amp;gt;
      &amp;lt;/header&amp;gt;
}
function Nav(props) {
  const lis = []
  for(let i=0; i&amp;lt;props.topics.length; i++) {
    let t = props.topics[i];
    lis.push(&amp;lt;li key={t.id}&amp;gt;&amp;lt;a href={'/read/'+t.id}&amp;gt;{t.title}&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;);
  }
  return &amp;lt;nav&amp;gt;
        &amp;lt;ol&amp;gt;
          {lis}
        &amp;lt;/ol&amp;gt;
      &amp;lt;/nav&amp;gt;
}
function Article(props) {
  return &amp;lt;article&amp;gt;
        &amp;lt;h2&amp;gt;{props.title}&amp;lt;/h2&amp;gt;
        {props.body}
      &amp;lt;/article&amp;gt;
}
function App() {
  const topics = [
    {id:1, title:'html', body:'html is ...'},
    {id:2, title:'css', body:'css is ...'},
    {id:3, title:'js', body:'js is ...'}
  ]
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Header title=&quot;React&quot; onChangeMode={function() {
        alert('Header');
      }}&amp;gt;&amp;lt;/Header&amp;gt;   
      &amp;lt;Nav topics={topics}&amp;gt;&amp;lt;/Nav&amp;gt;
      &amp;lt;Article title=&quot;Welcome&quot; body=&quot;Hello, Web!&quot;&amp;gt;&amp;lt;/Article&amp;gt;
      &amp;lt;Article title=&quot;Hi&quot; body=&quot;React!&quot;&amp;gt;&amp;lt;/Article&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;화살표 함수로 변경해서 적용할 시&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import logo from './logo.svg';
import './App.css';
function Header(props) {
  console.log('props', props, props.title);
  return &amp;lt;header&amp;gt;
        &amp;lt;h1&amp;gt;&amp;lt;a href=&quot;/&quot; onClick={(event) =&amp;gt;{
          event.preventDefault();
          props.onChangeMode();
        }}&amp;gt;{props.title}&amp;lt;/a&amp;gt;&amp;lt;/h1&amp;gt;
      &amp;lt;/header&amp;gt;
}
function Nav(props) {
  const lis = []
  for(let i=0; i&amp;lt;props.topics.length; i++) {
    let t = props.topics[i];
    lis.push(&amp;lt;li key={t.id}&amp;gt;&amp;lt;a href={'/read/'+t.id}&amp;gt;{t.title}&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;);
  }
  return &amp;lt;nav&amp;gt;
        &amp;lt;ol&amp;gt;
          {lis}
        &amp;lt;/ol&amp;gt;
      &amp;lt;/nav&amp;gt;
}
function Article(props) {
  return &amp;lt;article&amp;gt;
        &amp;lt;h2&amp;gt;{props.title}&amp;lt;/h2&amp;gt;
        {props.body}
      &amp;lt;/article&amp;gt;
}
function App() {
  const topics = [
    {id:1, title:'html', body:'html is ...'},
    {id:2, title:'css', body:'css is ...'},
    {id:3, title:'js', body:'js is ...'}
  ]
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Header title=&quot;React&quot; onChangeMode={() =&amp;gt; {
        alert('Header');
      }}&amp;gt;&amp;lt;/Header&amp;gt;   
      &amp;lt;Nav topics={topics}&amp;gt;&amp;lt;/Nav&amp;gt;
      &amp;lt;Article title=&quot;Welcome&quot; body=&quot;Hello, Web!&quot;&amp;gt;&amp;lt;/Article&amp;gt;
      &amp;lt;Article title=&quot;Hi&quot; body=&quot;React!&quot;&amp;gt;&amp;lt;/Article&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;2659&quot; data-start=&quot;2622&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;State&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;state.png&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;752&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HutA2/dJMcaawW1Jg/muw8BmSjsZw88QuRXxvYcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HutA2/dJMcaawW1Jg/muw8BmSjsZw88QuRXxvYcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HutA2/dJMcaawW1Jg/muw8BmSjsZw88QuRXxvYcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHutA2%2FdJMcaawW1Jg%2Fmuw8BmSjsZw88QuRXxvYcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;944&quot; height=&quot;752&quot; data-filename=&quot;state.png&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;752&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763974229769&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [mode, setMode] = useState('WELCOME');
const [id, setId] = useState(null);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2173&quot; data-start=&quot;2145&quot;&gt;mode가 바뀌면 자동으로 UI가 업데이트됨&lt;/li&gt;
&lt;li data-end=&quot;2215&quot; data-start=&quot;2174&quot;&gt;React는 데이터가 바뀌면 화면을 다시 렌더링한다는 개념&lt;/li&gt;
&lt;li data-end=&quot;2215&quot; data-start=&quot;2174&quot;&gt;주의 ) 입력값을 태그 속성값으로 넣을 때 문자로 변환 됨. (숫자도)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-start=&quot;2622&quot; data-end=&quot;2659&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Create&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;creat1.png&quot; data-origin-width=&quot;891&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u0mm8/dJMcahpjrcx/rkJxwPDuptwHIOUCm9E230/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u0mm8/dJMcahpjrcx/rkJxwPDuptwHIOUCm9E230/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u0mm8/dJMcahpjrcx/rkJxwPDuptwHIOUCm9E230/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu0mm8%2FdJMcahpjrcx%2FrkJxwPDuptwHIOUCm9E230%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;769&quot; height=&quot;414&quot; data-filename=&quot;creat1.png&quot; data-origin-width=&quot;891&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;create2.png&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;409&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TOu0A/dJMcafSyVEU/JgryL6H2XnwEkA6udwuUCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TOu0A/dJMcafSyVEU/JgryL6H2XnwEkA6udwuUCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TOu0A/dJMcafSyVEU/JgryL6H2XnwEkA6udwuUCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTOu0A%2FdJMcafSyVEU%2FJgryL6H2XnwEkA6udwuUCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;755&quot; height=&quot;377&quot; data-filename=&quot;create2.png&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;409&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 배열을 만들고 추가해야함!!&lt;/p&gt;
&lt;pre id=&quot;code_1763974549853&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;setTopics([...topics, newTopic]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 ...은 topics를 그대로 복붙하는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;2659&quot; data-start=&quot;2622&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Update&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2437&quot; data-start=&quot;2411&quot;&gt;useState로 기존 값을 기본값으로 채움&lt;/li&gt;
&lt;li data-end=&quot;2458&quot; data-start=&quot;2438&quot;&gt;onSubmit에서 업데이트 실행&lt;/li&gt;
&lt;li data-end=&quot;2458&quot; data-start=&quot;2438&quot;&gt;create + read 같은 느낌임. 하이브리드 처럼 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-start=&quot;2622&quot; data-end=&quot;2659&quot;&gt;Delete&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;실제로 삭제&amp;rdquo;가 아니라,&lt;br /&gt;해당 id를 제외한 &lt;b&gt;새로운 배열을 만들어 교체&lt;/b&gt;하는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 강의 및 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/playlist?list=PLuHgQVnccGMCOGstdDZvH41x0Vtvwyxu7&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/playlist?list=PLuHgQVnccGMCOGstdDZvH41x0Vtvwyxu7&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1763974780624&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;React 2022 개정판&quot; data-og-description=&quot; &quot; data-og-host=&quot;www.youtube.com&quot; data-og-source-url=&quot;https://www.youtube.com/playlist?list=PLuHgQVnccGMCOGstdDZvH41x0Vtvwyxu7&quot; data-og-url=&quot;http://www.youtube.com/playlist?list=PLuHgQVnccGMCOGstdDZvH41x0Vtvwyxu7&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fljXo/hyZOD3H886/iuqzvGlakr3QWEQltMLCQk/img.jpg?width=480&amp;amp;height=270&amp;amp;face=0_0_480_270,https://scrap.kakaocdn.net/dn/cDepPl/hyZN8R0eGz/BxKtNAiwmoDj5j9WUWkza1/img.jpg?width=480&amp;amp;height=270&amp;amp;face=0_0_480_270&quot;&gt;&lt;a href=&quot;https://www.youtube.com/playlist?list=PLuHgQVnccGMCOGstdDZvH41x0Vtvwyxu7&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.youtube.com/playlist?list=PLuHgQVnccGMCOGstdDZvH41x0Vtvwyxu7&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fljXo/hyZOD3H886/iuqzvGlakr3QWEQltMLCQk/img.jpg?width=480&amp;amp;height=270&amp;amp;face=0_0_480_270,https://scrap.kakaocdn.net/dn/cDepPl/hyZN8R0eGz/BxKtNAiwmoDj5j9WUWkza1/img.jpg?width=480&amp;amp;height=270&amp;amp;face=0_0_480_270');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;React 2022 개정판&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.youtube.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.react.dev/learn&quot;&gt;빠르게 시작하기 &amp;ndash; React&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1763974780569&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;빠르게 시작하기 &amp;ndash; React&quot; data-og-description=&quot;The library for web and native user interfaces&quot; data-og-host=&quot;ko.react.dev&quot; data-og-source-url=&quot;https://ko.react.dev/learn&quot; data-og-url=&quot;https://ko.react.dev/learn&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uFjwE/hyZOEuKDht/J286kGFBhE5sj0iJGy8MTk/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/c804NR/hyZOahT8sR/IJnqZTK3mxKKIURkS3wu61/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567&quot;&gt;&lt;a href=&quot;https://ko.react.dev/learn&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.react.dev/learn&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uFjwE/hyZOEuKDht/J286kGFBhE5sj0iJGy8MTk/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/c804NR/hyZOahT8sR/IJnqZTK3mxKKIURkS3wu61/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;빠르게 시작하기 &amp;ndash; React&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The library for web and native user interfaces&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ko.react.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>우아한테크코스</category>
      <author>s00jin</author>
      <guid isPermaLink="true">https://s00jin.tistory.com/48</guid>
      <comments>https://s00jin.tistory.com/48#entry48comment</comments>
      <pubDate>Mon, 24 Nov 2025 18:00:11 +0900</pubDate>
    </item>
    <item>
      <title>[오픈미션] Javascript 공부</title>
      <link>https://s00jin.tistory.com/47</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 자신있게 들어가려고 했다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 난 HTML, CSS만 가능한 사람&amp;hellip;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;것도 기본 문법이랑 늬양스 정도만 아는&amp;hellip;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 급 드래프트로 자바스크립트 공부 먼저 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 리액트 강의를 켰더니 자바스크립트를 알아야 한다고 한다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(이런 사실도 몰랐던 엄청난 코린이&amp;hellip;.)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;변수와 스코프&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;const&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상수&lt;/li&gt;
&lt;li&gt;선언 + 초기와 + 할당이 한번에 이루어짐&lt;/li&gt;
&lt;li&gt;재할당 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;let&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;변하는 값&lt;/li&gt;
&lt;li&gt;한 번 선언된 변수를 다시 선언하면 에러&lt;/li&gt;
&lt;li&gt;블록 스코프&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 번 선언된 변수를 다시 선언 할 수 있음 (재선언 가능)&lt;/li&gt;
&lt;li&gt;선언하기 전에 사용 가능 (호이스팅)&lt;/li&gt;
&lt;li&gt;하지만 할당은 할당된 위치 이후부터 가능&lt;/li&gt;
&lt;li&gt;그전에 사용하면 undefined&lt;/li&gt;
&lt;li&gt;함수 스코프&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;호이스팅&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스코프 내부 어디서든 변수 선언은 최상위에 선언된 것 처럼 행동 (할당 X)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;호이스팅.png&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IPkva/dJMcaaKtO6w/oC7I7Yo4Rrk9NvKsuA7Qn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IPkva/dJMcaaKtO6w/oC7I7Yo4Rrk9NvKsuA7Qn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IPkva/dJMcaaKtO6w/oC7I7Yo4Rrk9NvKsuA7Qn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIPkva%2FdJMcaaKtO6w%2FoC7I7Yo4Rrk9NvKsuA7Qn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;785&quot; height=&quot;376&quot; data-filename=&quot;호이스팅.png&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;634&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수의 생성과정&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;선언 단계&lt;/li&gt;
&lt;li&gt;초기화 단계&lt;/li&gt;
&lt;li&gt;할당 단계&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;선언 및 초기화 단계&lt;/li&gt;
&lt;li&gt;할당 단계&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;let&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;선언 단계&lt;/li&gt;
&lt;li&gt;초기화 단계&lt;/li&gt;
&lt;li&gt;할당 단계&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;const&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;선언 + 초기화 + 할당&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var : 함수 스코프&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;let, const : 블록 스코프&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이왕이면 var 말고 let, const 써서 예측 가능한 코드를 짜는게 좋다!!&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;브라우저 기본 함수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;alert() &amp;rarr; 단순 알림 느낌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;prompt() &amp;rarr; 사용자에게 값을 입력 받을 때 (문자열 반환)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인수 2개 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두번째는 기본값&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;confirm() - 확인/취소가 있는데 각각 true/false 반환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;스크립트를 강제로 일시 정지함&lt;/li&gt;
&lt;li&gt;스타일링 불가능&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;형변환&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;String() &amp;rarr; 문자형으로 변환 (대문자로 시작해야 함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Number() &amp;rarr; 숫자형으로 변환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Boolean() &amp;rarr; 불린형으로 변환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프롬프트 입력 시 입력 값은 문자열이 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;false&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;숫자 0&lt;/li&gt;
&lt;li&gt;빈 문자열&lt;/li&gt;
&lt;li&gt;null&lt;/li&gt;
&lt;li&gt;undefined&lt;/li&gt;
&lt;li&gt;NaN&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의사항&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Number(null) &amp;rarr; 0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Number(undefined) &amp;rarr;NaN&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Boolean(0) &amp;rarr; false&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Boolean(&amp;rsquo;0&amp;rsquo;) &amp;rarr; true&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Boolean(&amp;rsquo;&amp;rsquo;) &amp;rarr; false&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Boolean(&amp;rsquo; &amp;lsquo;) &amp;rarr; true&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본 연산자&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;거듭제곱: **&lt;/li&gt;
&lt;li&gt;일치 비교:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;=== 타입 + 값 모두 비교&lt;/li&gt;
&lt;li&gt;== 자동 형변환 &amp;rarr; 1 == &quot;1&quot; &amp;rarr; true&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;조건문&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;if / else if / else&lt;/li&gt;
&lt;li&gt;switch&lt;/li&gt;
&lt;li&gt;(case마다 break 필요)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;반복문&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;for&lt;/h3&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;for (let i = 0; i &amp;lt; 10; i++) {}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;while&lt;/h3&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;while (i &amp;lt; 10) { i++; }

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;do &amp;hellip; while&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번은 무조건 실행&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;break / continue&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복 제어&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function sayHello(name) {
	console.log(`Hello, ${name}`);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;함수 선언문&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;sayHello();  // 호이스팅 가능

function sayHello() {
	console.log('Hello');
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어디서든 호출 가능&lt;/li&gt;
&lt;li&gt;인터프리터 방식이지만 코드를 실행하기 전 선언된 함수를 모아 먼저 실행해둠&lt;/li&gt;
&lt;li&gt;= 호이스팅 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;함수 표현식&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;let sayHello = funtion() {
	console.log('Hello');
}
sayHello();
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드에 도달하면 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;화살표 함수&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;const add = (a, b) =&amp;gt; a + b;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화살표 함수의 특징&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자신만의 this 없음 &amp;rarr; 외부 this를 가져옴&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 Object&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;method: 객체 프로퍼티로 할당 된 함수&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const superman = {
	name:'clark',
	age:33,
	fly:function(){  // funcion 키워드 생략 가능 :이것도 같이 생략 가능
		console.log('날아갑니다')
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화살표 함수는 일반 함수와는 달리 자신만의 this를 가지지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화살표 함수 내부에서 this를 사용하면, 그 this는 외부에서 값을 가져 옴&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;let student = [&amp;rsquo;철수&amp;rsquo;, &amp;lsquo;영희&amp;rsquo;, &amp;hellip; , &amp;lsquo;영수&amp;rsquo;];&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배열은 문자뿐만 아니라, 숫자, 객체, 함수 등도 포함할 수 있음&lt;/li&gt;
&lt;li&gt;length : 배열의 길이&lt;/li&gt;
&lt;li&gt;push : 배열 끝에 추가&lt;/li&gt;
&lt;li&gt;pop : 배열 끝 요소 제거&lt;/li&gt;
&lt;li&gt;unshift : 배열 앞에 추가&lt;/li&gt;
&lt;li&gt;shift : 배열 앞에 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;for&lt;/li&gt;
&lt;li&gt;for &amp;hellip; of&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;let days = ['월', '화', '수'];

for(let day of days) {
	console.log(day);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for &amp;hellip; in 이 아님 이건 배열에서는 비추&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자 함수&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;보통 첫글자는 대문자로&lt;/li&gt;
&lt;li&gt;new 연산자를 사용하여 호출&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function User(name, age) {
	this.name = name;
	this.age = age;
	this.sayName = function() {
		console.log(this.name);
	}
}

let user1 = new User('Mike', 30);
let user2 = new User('Jane', 22);

user1.sayName(); // 'Mike'
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;computed property&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;const key = &quot;age&quot;;
const user = {
  name: &quot;Mike&quot;,
  [key]: 30
};

&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object 메소드&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;assign() : 객체 복사&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;const newUser = Object.assign({}, user);

{} + { name : 'Mike', age : 30 } =

-----------

Object.assign({ gender:'male' }, user);

위처럼 초기값 주면
gender : 'male',
name : 'Mike',
age : 30,

이렇게 됨
초기값에 들어간 키가 같다면 뒤에 user에꺼를 덮어씀

------------------

const user = {
	name : 'Mike'
}
const info1 = {
	age : 30,
}
const info2 = {
	gender : 'male',
}

Object.assign(user, info1, info2)
이렇게 하면 user에 info1과 info2 합치는 것
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;{} 초기값 , user 가 초기값에 들어감&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 새로운 객체에 복사되는 것&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Object.keys() : 키 배열 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;const user = {
	name : 'Mike',
	age : 30,
	gender : 'mail',
}

Object.keys(user);
// [&quot;name&quot;, &quot;age&quot;, &quot;gender&quot;]

&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Object.values() : 값 배열 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;const user = {
	name : 'Mike',
	age : 30,
	gender : 'male',
}

Object.values(user);
// [&quot;Mike&quot;, 30, &quot;male&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Object.entries() : 키/값 배열 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;const user = {
	name : 'Mike',
	age : 30,
	gender : 'male',
}

Object.entries(user); 

//
[
	[&quot;name&quot;, &quot;Mike&quot;],
	[&quot;age&quot;, 30],
	[&quot;gender&quot;, &quot;male&quot;]
]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Object.fromEntries() : 키/값 배열을 객체로&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;const arr = 
[
	[&quot;name&quot; &quot;Mike&quot;],
	[&quot;age&quot;, 30],
	[&quot;gender&quot;, &quot;male&quot;]
];

Object.fromEntries(arr);

//
{
	name: 'Mike',
	age : 30,
	gender : 'mail',
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Symbol&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유일성 보장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;const a = Symbol(); // new 붙이지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;const b = Symbol();&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;const c = Symbol(&amp;rsquo;id&amp;rsquo;); // 이렇게 설명 추가 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;console.log(a) // Symbol() 출력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a === b // false&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;const id = Symbol('id');
const user = {
	name : 'Mike',
	age : 30,
	[id] : 'myid'
}

user 
// {name: &quot;Mike&quot;, age: 30, Symbol(id): &quot;myid&quot;}
user[id] // &quot;myid&quot;

// 하지만 Object 메소드 사용시 키가 심볼형인 애들은 건너뜀
Object.keys(user); // [&quot;name&quot;, &quot;age&quot;]
Object.values(user); // [&quot;Mike&quot;, 30]
Object.entries(user); // [Array(2), Array(2)]

// 숨겨진 심볼 키를 보고 싶다면
Object.getOwnPropertySymbols(user); // [Symbol(id)]
Reflect.ownKeys(user); // [&quot;name&quot;, &quot;age&quot;, Symbol(id)]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Symbol.for() : 전역 심볼&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 심볼만 보장 받을 수 있음&lt;/li&gt;
&lt;li&gt;없으면 만들고, 있으면 가져오기 때문&lt;/li&gt;
&lt;li&gt;Symbol 함수는 매번 다른 Symbol 값을 생성하지만,&lt;/li&gt;
&lt;li&gt;Symbol.for 메소드는 하나를 생성한 뒤 키를 통해 같은 Symbol을 공유&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;const id1 = Symbol.for('id');
const id2 = Symbol.for('id');

id1 === id2 // true

// 이름을 얻고 싶다면
Symbol.keyFor(id1) // &quot;id&quot;

// 만약 전역 심볼이 아닌데 이름을 알고 싶다면
const id = Symbol('id 입니다');
id.description; // &quot;id 입니다&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// 다른 개발자가 만들어 놓은 객체
const user = {
	name: &quot;Mike&quot;,
	age: 30,
};

// 내가 작업
// user.showName = function() {}; // 이렇게 하면 아래 사용자가 보는 메세지가 이상해짐
const showName = Symbol(&quot;show name&quot;);
user[showName] = function () {
	console.log(this.name);
};

user[showName](); // Mike

// 사용자가 접속하면 보는 메세지
for (let key in user) {
	const.log(`His ${key} is ${user[key]}.`);
}

// His name is Mike.
// His age is 30.
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>우아한테크코스</category>
      <author>s00jin</author>
      <guid isPermaLink="true">https://s00jin.tistory.com/47</guid>
      <comments>https://s00jin.tistory.com/47#entry47comment</comments>
      <pubDate>Mon, 24 Nov 2025 17:34:04 +0900</pubDate>
    </item>
    <item>
      <title>자바 자료형 총정리 | 기본형, 참조형, 래퍼 클래스</title>
      <link>https://s00jin.tistory.com/46</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자바 자료형&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 자료형 (Primitive Data Type)&lt;/li&gt;
&lt;li&gt;참조 자료형 (Reference Data Type)&lt;/li&gt;
&lt;li&gt;래퍼 클래스 (Wrapper Class)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;기본 자료형 | Primitive Data Type&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수가 &lt;b&gt;&amp;ldquo;실제 값&amp;rdquo;&lt;/b&gt;을 가짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입 크기 표현 범위&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 70.6975%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.52786%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 15.0724%;&quot;&gt;타입&lt;/td&gt;
&lt;td style=&quot;width: 13.5608%;&quot;&gt;크기&lt;/td&gt;
&lt;td style=&quot;width: 30.7517%;&quot;&gt;표현 범위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.52786%;&quot;&gt;진위형&lt;/td&gt;
&lt;td style=&quot;width: 15.0724%;&quot;&gt;boolean&lt;/td&gt;
&lt;td style=&quot;width: 13.5608%;&quot;&gt;1bit&lt;/td&gt;
&lt;td style=&quot;width: 30.7517%;&quot;&gt;true / false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.52786%;&quot;&gt;문자형&lt;/td&gt;
&lt;td style=&quot;width: 15.0724%;&quot;&gt;char&lt;/td&gt;
&lt;td style=&quot;width: 13.5608%;&quot;&gt;2바이트&lt;/td&gt;
&lt;td style=&quot;width: 30.7517%;&quot;&gt;0 ~ 65,535 (유니코드)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.52786%;&quot;&gt;정수형&lt;/td&gt;
&lt;td style=&quot;width: 15.0724%;&quot;&gt;byte&lt;/td&gt;
&lt;td style=&quot;width: 13.5608%;&quot;&gt;1바이트&lt;/td&gt;
&lt;td style=&quot;width: 30.7517%;&quot;&gt;-128 ~ 127&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.52786%;&quot;&gt;정수형&lt;/td&gt;
&lt;td style=&quot;width: 15.0724%;&quot;&gt;short&lt;/td&gt;
&lt;td style=&quot;width: 13.5608%;&quot;&gt;2바이트&lt;/td&gt;
&lt;td style=&quot;width: 30.7517%;&quot;&gt;-32,768 ~ 32,767&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.52786%;&quot;&gt;정수형&lt;/td&gt;
&lt;td style=&quot;width: 15.0724%;&quot;&gt;int&lt;/td&gt;
&lt;td style=&quot;width: 13.5608%;&quot;&gt;4바이트&lt;/td&gt;
&lt;td style=&quot;width: 30.7517%;&quot;&gt;-2,147,483,648 ~ 2,147,483,647&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.52786%;&quot;&gt;정수형&lt;/td&gt;
&lt;td style=&quot;width: 15.0724%;&quot;&gt;long&lt;/td&gt;
&lt;td style=&quot;width: 13.5608%;&quot;&gt;8바이트&lt;/td&gt;
&lt;td style=&quot;width: 30.7517%;&quot;&gt;약 &amp;plusmn;9.22&amp;times;10&amp;sup1;⁸&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.52786%;&quot;&gt;실수형&lt;/td&gt;
&lt;td style=&quot;width: 15.0724%;&quot;&gt;float&lt;/td&gt;
&lt;td style=&quot;width: 13.5608%;&quot;&gt;4바이트&lt;/td&gt;
&lt;td style=&quot;width: 30.7517%;&quot;&gt;약 &amp;plusmn;3.4&amp;times;10⁻&amp;sup3;⁸ ~ &amp;plusmn;3.4&amp;times;10&amp;sup3;⁸&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.52786%;&quot;&gt;실수형&lt;/td&gt;
&lt;td style=&quot;width: 15.0724%;&quot;&gt;double&lt;/td&gt;
&lt;td style=&quot;width: 13.5608%;&quot;&gt;8바이트&lt;/td&gt;
&lt;td style=&quot;width: 30.7517%;&quot;&gt;약 &amp;plusmn;1.7&amp;times;10⁻&amp;sup3;⁰⁸ ~ &amp;plusmn;1.7&amp;times;10&amp;sup3;⁰⁸&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;참조 자료형 | Reference Data Type&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 저장된 &lt;b&gt;&amp;ldquo;주소 값&amp;rdquo;&lt;/b&gt;을 가짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 값 X, 객체를 참조하는 데이터 타입&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 참조 자료형&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;String, ArrayList, HashMap, HashSet, LinkedList, Queue, Stack 등&lt;/li&gt;
&lt;li&gt;String, 클래스, 인터페이스, 배열, 열거 타입 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;래퍼 클래스 | Wrapper Class&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 자료형을 &amp;lsquo;객체 (Object)&amp;rsquo;로 다루기 위해 객체화 시키는 클래스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 기본 타입의 객체화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 68.2558%; height: 178px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 12.2093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 28.3721%;&quot;&gt;기본 자료형 (Primitive Type)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 27.5581%;&quot;&gt;&amp;nbsp;래퍼 클래스 (Wrapper Class)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 12.2093%;&quot;&gt;진위형&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 28.3721%;&quot;&gt;boolean&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 27.5581%;&quot;&gt;Boolean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 12.2093%;&quot;&gt;문자형&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 28.3721%;&quot;&gt;char&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 27.5581%;&quot;&gt;Character&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 12.2093%;&quot;&gt;정수형&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 28.3721%;&quot;&gt;byte&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 27.5581%;&quot;&gt;Byte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 12.2093%;&quot;&gt;정수형&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 28.3721%;&quot;&gt;short&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 27.5581%;&quot;&gt;Short&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 12.2093%;&quot;&gt;정수형&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 28.3721%;&quot;&gt;int&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 27.5581%;&quot;&gt;Integer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 12.2093%;&quot;&gt;정수형&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 28.3721%;&quot;&gt;long&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 27.5581%;&quot;&gt;Long&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 12.2093%;&quot;&gt;실수형&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 28.3721%;&quot;&gt;float&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 27.5581%;&quot;&gt;Float&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 12.2093%;&quot;&gt;실수형&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 28.3721%;&quot;&gt;double&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 27.5581%;&quot;&gt;Double&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;박싱과 언박싱&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;박싱과 언박싱.png&quot; data-origin-width=&quot;492&quot; data-origin-height=&quot;496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0WziA/dJMb9YcblXQ/89HBzzzijmQHys9uxpGkBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0WziA/dJMb9YcblXQ/89HBzzzijmQHys9uxpGkBk/img.png&quot; data-alt=&quot;출처 http://www.tcpschool.com/java/java_api_wrapper&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0WziA/dJMb9YcblXQ/89HBzzzijmQHys9uxpGkBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0WziA%2FdJMb9YcblXQ%2F89HBzzzijmQHys9uxpGkBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;492&quot; height=&quot;496&quot; data-filename=&quot;박싱과 언박싱.png&quot; data-origin-width=&quot;492&quot; data-origin-height=&quot;496&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 http://www.tcpschool.com/java/java_api_wrapper&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;박싱 (Boxing)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 타입 &amp;rarr; 래퍼 클래스&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// Java 1.5 이상 버전부터 오토 박싱이 적용된다
int primitiveInt = 1;
Integer wrapperInteger = primitiveInt;

// 수동 박싱 시
int primitiveInt2 = 2;
Integer wrapperInteger2 = Integer.valueOf(primitiveInt2);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;언박싱(Unboxing)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;래퍼 클래스 &amp;rarr; 기본 타입&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;// Java 1.5 이상 버전부터 오토 언박싱이 적용된다
Integer integer1 = 1;
int primitiveInt = integer1;

// 수동 언박싱 시
Integer integer2 = 2;
int primitiveInt2 = integer2.intValue();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 170px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.30233%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 27.2093%;&quot;&gt;기본 자료형 (Primitive Type)&lt;/td&gt;
&lt;td style=&quot;width: 32.6745%;&quot;&gt;&amp;nbsp;래퍼 클래스 (Wrapper Class)&lt;/td&gt;
&lt;td style=&quot;width: 30.5814%;&quot;&gt;&amp;nbsp;언박싱 (Unboxing)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 9.30233%;&quot;&gt;진위형&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 27.2093%;&quot;&gt;boolean&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 32.6745%;&quot;&gt;Boolean&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 30.5814%;&quot;&gt;a.booleanValue()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 9.30233%;&quot;&gt;문자형&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 27.2093%;&quot;&gt;char&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 32.6745%;&quot;&gt;Character&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 30.5814%;&quot;&gt;a.charValue()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 9.30233%;&quot;&gt;정수형&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 27.2093%;&quot;&gt;byte&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 32.6745%;&quot;&gt;Byte&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 30.5814%;&quot;&gt;a.byteValue()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 9.30233%;&quot;&gt;정수형&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 27.2093%;&quot;&gt;short&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 32.6745%;&quot;&gt;Short&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 30.5814%;&quot;&gt;a.shortValue()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 9.30233%;&quot;&gt;정수형&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 27.2093%;&quot;&gt;int&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 32.6745%;&quot;&gt;Integer&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 30.5814%;&quot;&gt;a.intValue()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 9.30233%;&quot;&gt;정수형&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 27.2093%;&quot;&gt;long&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 32.6745%;&quot;&gt;Long&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 30.5814%;&quot;&gt;a.longValue()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 9.30233%;&quot;&gt;실수형&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 27.2093%;&quot;&gt;float&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 32.6745%;&quot;&gt;Float&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 30.5814%;&quot;&gt;a.floatValue()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 9.30233%;&quot;&gt;실수형&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 27.2093%;&quot;&gt;double&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 32.6745%;&quot;&gt;Double&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 30.5814%;&quot;&gt;a.doubleValue()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;추가 메모&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;제너릭 타입 내에서 사용 가능 여부 &lt;/b&gt;&lt;br /&gt;기본 자료형 X, 래퍼 클래스 O&lt;br /&gt;&lt;br /&gt;&lt;b&gt;null 초기화 가능 여부&lt;/b&gt;&lt;span style=&quot;color: #333333; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; &lt;br /&gt;기본 자료형 X, 래퍼 클래스 O&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;b&gt;참조 자료형 변수는 null로 초기화 가능&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;래퍼 클래스는 불변(Immutable) 객체&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 링크&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/119&quot;&gt;https://adjh54.tistory.com/119&lt;/a&gt;&lt;/p&gt;</description>
      <category>Java</category>
      <category>자바</category>
      <author>s00jin</author>
      <guid isPermaLink="true">https://s00jin.tistory.com/46</guid>
      <comments>https://s00jin.tistory.com/46#entry46comment</comments>
      <pubDate>Wed, 22 Oct 2025 16:44:20 +0900</pubDate>
    </item>
    <item>
      <title>[회고] 1주차 프리코스 회고</title>
      <link>https://s00jin.tistory.com/45</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;우테코.png&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bq2v4y/dJMb9V7AXXM/nadr8JBHhuhXYQTYCnmIEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bq2v4y/dJMb9V7AXXM/nadr8JBHhuhXYQTYCnmIEk/img.png&quot; data-alt=&quot;출처 https://www.woowacourse.io/apply&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bq2v4y/dJMb9V7AXXM/nadr8JBHhuhXYQTYCnmIEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbq2v4y%2FdJMb9V7AXXM%2Fnadr8JBHhuhXYQTYCnmIEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;225&quot; height=&quot;225&quot; data-filename=&quot;우테코.png&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 https://www.woowacourse.io/apply&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;우테코에 지원하며&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 우아한테크코스 8기에 지원했다. &lt;s&gt;사실 몇개월 전까지 우테코가 뭔지도 몰랐다..ㅎ&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구름톤 유니브 대표를 맞게 되면서 지부 해커톤을 운영했는데, 이때 만난 멘토분께서 우테캠 출신이란걸 알게 되면서 우테코와 우테캠에 대해서 알게되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그래서 내가 왜 우테코에 지원했냐?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 지금 내 자신은 껍데기만 요란하고 속은 빈 것 같아서 지원했다. 한마디로 얇고 넓은 지식???이라기엔 넓지도 않은 것 같다. 취업 준비를 하며 더더욱 빈 껍데기 같다는 걸 느끼게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 휴학도 한번 하지 않았고, 4년 꾸준히 학교를 다녔으니, 취업 준비에 너무 급급하지 않고, 이번 기회에 진득하게 약 1년을 바쳐 하고 싶은 공부를 하고 싶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4년동안 학교를 쉬지않고 다닌 이유도, 정해진 공부가 아닌 하고 싶은 공부를 하고 싶어서다. 그래서 학교라는 곳을 탈출하고 싶었기에 쉬지않고 다녔다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1주차 프리코스 과제를 하며&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1주차 프리코스 과제를 처음 마주했을땐 생각보다 쉽다고 느껴졌다. 하지만 과제가 아닌 지켜야 할 규칙인 구글 자바 스타일 가이드와 커밋 컨벤션을 코드에 적용하는게 쉽지 않을 것 같다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우테코에서 정해준 구글 자바 스타일 가이드와 커밋 컨벤션의 레퍼런스를 해석하고 공부하는데만 꼬박 3일이 걸렸다. 이것 또한 완벽하게 공부한게 아닌 큰 틀의 이해였고, 코드를 작성하며 꾸준히 레퍼런스를 참고했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과제는 문제 자체는 간단했다. 하지만 내가 느끼기에는 추상적인 부분이 많다고 느껴졌다. 근데 알고보니 문제에 답이 다 있었다. 문제를 하나하나 뜯어보며, 예외 상황을 분석했고, 문제를 구체화 해나갔다. 그 과정에서 단순한 기능 구현의 문제라도 생각해야할 예외 처리가 많다는 걸 배웠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1주차 느낀점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평소 팀 프로젝트를 할 때 기능 구현에만 급급하고 예외 처리는 신경 쓰지 않았다. 이번 1주차 과제를 하며, 예외 처리의 중요성을 느껴 반성하게 되었다. 그리고 커밋 또한 내가 생각했을 때 적당한 시기일 때 커밋을 했었는데, 이번에 기능 목록을 작성하고, 이 목록의 단위로 커밋을 진행하여, 체계적인 커밋 단위를 통해 협업 효율성과 코드 리뷰의 효율성을 높일 수 있을 것같다는 점을 깨달았다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;코드 리뷰를 주고 받으며&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음으로 제대로된 코드 리뷰 방법을 알고, 코드 리뷰를 주고 받았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 코드 리뷰를 하며 많은 것을 배웠다. 어떤 식으로 구조를 잡는게 좋은지, 역할을 분리하기 위해 메서드를 사용하여 코드 작성하기, Javadoc 작성하기 등 다른 사람의 코드를 리뷰하며 내 코드를 다시 돌아보게 되고, 반성하며 배우게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 내 코드에 stream 사용해보기, 배열이 아닌 List 사용해보기 등의 리뷰를 받았다. 리뷰어 선생님들이 어떻게 하면 좋을지에 대한 코드까지 리뷰로 달아주셨다&amp;hellip;(그저 경외감..✨)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;멘토님들의 힘나는 글들&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네오님의 1주 차 피드백 글에 있던 글이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우아한테크코스에서는 '완벽하게 해내는 것'보다 '새로운 시도를 멈추지 않는 것'을 더 중요하게 생각합니다. 도전은 단순히 어려운 문제를 푸는 행위가 아니라, 자신이 아직 모르는 영역에 한 발 더 내딛는 태도입니다.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;미션을 수행하다 보면 예상치 못한 오류나 막히는 순간을 여러 번 겪게 될 것입니다. 그럴 때 포기하지 않고 문제를 끝까지 탐색해 보는 경험이 바로 성장의 출발점입니다. 하지만 얼마나 탐색해야할지, 얼마나 도전해야할지 막연할 수 있습니다. 그래서 &lt;/span&gt;&lt;b&gt;작은 도전부터 시작&lt;/b&gt;&lt;span style=&quot;color: #333333; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;해 보세요.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 1주차 과제를 하고 코드 리뷰를 받으며, 기존에 내가 잘 모르던 부분인 정규화와 스트림, 컬렉션들을 깨닫고, 하나씩 공부해 2주차에 적용해봐야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포비님의 글&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭐지? 난 구현 자체도 힘든데 OOP, TDD, MVC, DDD, AOP, 헥사고날 아키텍처 등등등 (포비 마음 속 생각: 벌써 저걸 잘하면 우테코가 아니라 취업하는게 낫지 않나?) 모두 처음 들어보는 용어들. 다들 너무 잘 하네. 난 힘들겠다. 그냥 포기해 버릴까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;앞으로도 삶을 살아가면서 비슷한 상황이 반복될 텐데요. 마음 속에서 이런 속삭임이 들린다면 어떻게 하면 좋을까요? 여러분은 이런 상황 속에서 어떤 선택, 어떤 마음 가짐으로 임하는 것이 좋을까요? 여러분이 이번 프리코스에서 진정 얻어가야할 것은 무엇일까요?&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 번 토론하기에도 남겼던 글인데 다시 한번 남겨 봅니다. 성장은 프로그래밍 역량과 같은 하드 스킬 뿐만 아니라 소프트 스킬 역량도 있습니다. AI가 이렇게 빠르게 발전하고 있는 지금 시대는 소프트 스킬 역량의 중요성이 점점 더 높아지고 있는지도 모릅니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 학창 시절에는 성적이라는 정량화된 평가 기준이 있기 때문에 성적에 따라 내가 성장하고 있음을 느낄 수 있는데요. 학교를 졸업한 후에는 성적과 같은 정량적인 기준이 없기 때문에 내가 잘 성장하고 있구나를 인식하는 것이 참 힘들고 어려운 일인 것 같아요. 하지만 사회 생활을 할 수록 현재 나의 상태를 인식하는 것(메타인지라 할까요?)은 더더욱 중요해 지는 것 같아요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이번 프리코스 과정을 통해 여러분이 잘 성장하고 있는지를 어떻게 판단할 것인지에 대한 기준을 세우고, 이렇게 정한 기준이 맞지 않는다 판단하면 다른 기준을 세워보면서 나만의 기준을 찾아가는 것 또한 저는 성장이라고 생각해요.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성장이라는 것을 너무 하드 스킬(프로그래밍 역량)적인 부분에 한정하지 말고 성장을 바라보는 나의 기준, 어려운 문제를 접했을 때의 나의 마음가짐, 잘하는 주변 사람에게 흔들리지 않는 자세, 실패하더라도 도전하는 것과 같은 소프트 스킬과 같은 부분도 성장하는 것이라 생각해 보면 어떨까요?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포비님이 남겨주신 글이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 글을 시작할 때 나온 인용글이 1주차를 진행하며, 그리고 커뮤니티를 보며 느낀 내 마음 속 생각과 똑같다.. 하지만 1년을 바칠 준비를 하고 시작한 것이기에, 어떤 상황에서도 프리코스 과정 만큼은 후회 없을 정도의 열정을 쏟고 싶다. 그리고 1주차를 진행하며, 기존에 내가 공부해오던 방식과 습관에 대해 많은 반성을 했다. 이번 프리코스에서 겉핥기 식으로 공부하던 습관을 버리고, 기초부터 차근차근 쌓아가야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 함께 공유하고 성장하는 즐거움을 배워야겠다..!&lt;/p&gt;</description>
      <category>우아한테크코스</category>
      <category>우테코</category>
      <category>회고</category>
      <author>s00jin</author>
      <guid isPermaLink="true">https://s00jin.tistory.com/45</guid>
      <comments>https://s00jin.tistory.com/45#entry45comment</comments>
      <pubDate>Wed, 22 Oct 2025 00:08:45 +0900</pubDate>
    </item>
    <item>
      <title>Git 커밋 메시지 정리본</title>
      <link>https://s00jin.tistory.com/44</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;우테코 프리코스에서는 커밋 메시지를 준수해야한다고 한다..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/stephenparish/9941e89d80e2bc58a153&quot;&gt;AngularJS Git Commit Message Conventions&lt;/a&gt; 의 커밋 메시지를 참고 작성하라고 하는데..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 영알못이라&amp;hellip;. 원활한 미션 진행을 위해 나름의 번역을 통해 정리본을 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;커밋 메시지 목표&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스크립트로 &lt;a href=&quot;http://CHANGELOG.md&quot;&gt;CHANGELOG.md&lt;/a&gt; 생성&lt;/li&gt;
&lt;li&gt;중요한 커밋이 아닌 것들을 git bisect 로 무시&lt;/li&gt;
&lt;li&gt;커밋 기록을 확인할 때 더 나은 정보 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;CHANGELOG.md 생성&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;마지막 릴리즈 이후 모든 Subjects list (커밋 메시지의 첫 줄) 목록 출력:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;git log &amp;lt;last tag&amp;gt; HEAD --pretty=format:%s
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 릴리스의 새로운 기능만 출력:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;git log &amp;lt;last release&amp;gt; HEAD --grep feature
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;mdash;grep feature
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;커밋 메시지 안에 feature을 검색 (즉, feature이 들어간 커밋들만 검색 후 출력)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;mdash;grep &amp;lt;검색어&amp;gt; &lt;br /&gt;커밋 메세지들에서 검색어가 포함된 애들만 출력해주는 것 같다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;중요하지 않은 커밋 식별&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;포매팅 변화 (공백/ 빈 줄 추가/ 제거, 들여쓰기)&lt;/li&gt;
&lt;li&gt;세미콜론 누락&lt;/li&gt;
&lt;li&gt;주석 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경 내역을 조회할 때 중요하지 않은 커밋 무시 = git bisect 사용&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;git bisect skip $( git rev-list --grep irrelevant &amp;lt;good place&amp;gt; HEAD )
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lsquo;irrelevant&amp;rsquo; 라는 문구가 있는 커밋은 무시&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;커밋 메시지의 형식&lt;/h1&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;&amp;lt;type&amp;gt;(&amp;lt;scope&amp;gt;): &amp;lt;subject&amp;gt;  # Subject line
&amp;lt;BLANK LINE&amp;gt;  # 빈줄
&amp;lt;body&amp;gt;  # Message body
&amp;lt;BLANK LINE&amp;gt;  # 빈줄
&amp;lt;footer&amp;gt;  # Message footer
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Subject line, Message body, Message footer로 구분됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 부분은 빈 줄로 구분&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;대개의 경우 Subject line으로만 작성되기도 함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Message body와 Message footer는 선택&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;한 줄에 100자를 넘기지 않아야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Subject line&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경 사항에 대한 간략한 설명이 포함됨&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;lt;type&amp;gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;feat - 새로 추가된 기능&lt;/li&gt;
&lt;li&gt;fix - 버그 수정&lt;/li&gt;
&lt;li&gt;docs - 문서 관련&lt;/li&gt;
&lt;li&gt;style - 포매팅, 세미콜론 누락 등&lt;/li&gt;
&lt;li&gt;refactor - 리팩토링&lt;/li&gt;
&lt;li&gt;test - 테스트 관련&lt;/li&gt;
&lt;li&gt;chore - 그 외 자잘한 수정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;lt;scope&amp;gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커밋 변경사항이 있는 위치를 지정하는 무엇이든 올 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) $location, $browser, $compile, $rootScope, $ngHref, $ngClick, $ngView 등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;커밋이 영향을 주는 파일이나 기능 단위를 표현해도 됩니다 ex) login, signup, api, home, user-service, router 등&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;lt;subject&amp;gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명령형, 현재 시제 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;✅&amp;nbsp;change, ❌&amp;nbsp;changed, ❌&amp;nbsp;changes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;대문자로 시작하지 않기&lt;/li&gt;
&lt;li&gt;끝에 . (마침표) 붙이지 않기&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Message body (선택적 작성)&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명령형, 현재 시제 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;✅&amp;nbsp;change, ❌&amp;nbsp;changed, ❌&amp;nbsp;changes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변화에 대한 동기(이유)를 작성해야 함&lt;/li&gt;
&lt;li&gt;이전과 변화 후의 차이를 기재&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Message footer (선택적 작성)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 변경 내역과 어떤 이슈를 해결했는지 기재&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Breaking changes&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경점, 변경 사유, 마이그레이션 지시(description of the change, justification and migration notes)가 언급되어야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;BREAKING CHANGE: isolate scope bindings definition has changed and
    the inject option for the directive controller injection was removed.
    
    To migrate the code follow the example below:
    
    Before:
    
    scope: {
      myAttr: 'attribute',
      myBind: 'bind',
      myExpression: 'expression',
      myEval: 'evaluate',
      myAccessor: 'accessor'
    }
    
    After:
    
    scope: {
      myAttr: '@',
      myBind: '@',
      myExpression: '&amp;amp;',
      // myEval - usually not useful, but in cases where the expression is assignable, you can use '='
      myAccessor: '=' // in directive's template change myAccessor() to myAccessor
    }
    
    The removed `inject` wasn't generaly useful for directives so there should be no code using it.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Referencing issues&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결된 이슈도 Closes#&amp;lt;이슈번호&amp;gt; 를 사용하여 기재&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Closes #123

// 해결된 이슈 여러개
Closes #123, #234, #345
&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h1&gt;예시&lt;/h1&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;feat($browser): onUrlChange event (popstate/hashchange/polling)

Added new event to $browser:
- forward popstate event if available
- forward hashchange event if popstate not available
- do polling when neither popstate nor hashchange available

Breaks $browser.onHashChange, which was removed (use onUrlChange instead)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;fix($compile): couple of unit tests for IE9

Older IEs serialize html uppercased, but IE9 does not...
Would be better to expect case insensitive, unfortunately jasmine does
not allow to user regexps for throw expectations.

Closes #392
Breaks foo.bar api, foo.baz should be used instead
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;feat(directive): ng:disabled, ng:checked, ng:multiple, ng:readonly, ng:selected

New directives for proper binding these attributes in older browsers (IE).
Added coresponding description, live examples and e2e tests.

Closes #351
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;style($location): add couple of missing semi colons
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;docs(guide): updated fixed docs from Google Docs

Couple of typos fixed:
- indentation
- batchLogbatchLog -&amp;gt; batchLog
- start periodic checking
- missing brace
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고 링크&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/stephenparish/9941e89d80e2bc58a153#message-body&quot;&gt;Git Commit Message Conventions&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://so-tired.tistory.com/325&quot;&gt;https://so-tired.tistory.com/325&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://simsi6.tistory.com/97#google_vignette&quot;&gt;버그가 발생한 커밋 찾기 (git bisect)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://creampuffy.tistory.com/129&quot;&gt;우아한테크코스 4기 프리코스 후기 (2) - Commit Log 컨벤션&lt;/a&gt;&lt;/p&gt;</description>
      <category>우아한테크코스</category>
      <category>우테코</category>
      <category>커밋 메시지</category>
      <author>s00jin</author>
      <guid isPermaLink="true">https://s00jin.tistory.com/44</guid>
      <comments>https://s00jin.tistory.com/44#entry44comment</comments>
      <pubDate>Sat, 18 Oct 2025 15:14:21 +0900</pubDate>
    </item>
    <item>
      <title>[부하 테스트] 부하 테스트를 위한 기본 개념</title>
      <link>https://s00jin.tistory.com/43</link>
      <description>&lt;div&gt;
&lt;div data-message-model-slug=&quot;gpt-5&quot; data-message-id=&quot;dffe0c8b-a0b2-4a2f-8931-b22cb9f033c7&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;성능 테스트를 공부하면서 정말 맨땅에 헤딩하듯 삽질을 많이 했다.&lt;br /&gt;혹시 나처럼 처음 시작하는 사람들에게 조금이라도 도움이 되길 바라며, 내가 정리한 내용을 공유한다.&lt;/blockquote&gt;
&lt;p data-end=&quot;630&quot; data-start=&quot;560&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;630&quot; data-start=&quot;560&quot; data-ke-size=&quot;size16&quot;&gt;이 글들은 내가 공부하면서 찾아본 자료들을 정리한 것이다.&lt;br /&gt;특히 인프런 박재성님 강의가 가장 큰 도움이 되었다.&lt;/p&gt;
&lt;p data-end=&quot;715&quot; data-start=&quot;632&quot; data-ke-size=&quot;size16&quot;&gt;부하 테스트에 관심이 있다면 이 강의를 추천한다.&lt;br /&gt;&lt;s&gt;(솔직히 강의를 듣기 전에는 부하테스트 공부를 포기할까 고민했는데, 듣자마자 신세계였다. 강추!!)&lt;/s&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-end=&quot;737&quot; data-start=&quot;722&quot; data-ke-size=&quot;size26&quot;&gt;부하 테스트를 하는 이유&lt;/h2&gt;
&lt;p data-end=&quot;795&quot; data-start=&quot;738&quot; data-ke-size=&quot;size16&quot;&gt;내가 만든 프로젝트를 배포하기 전에, 이 서비스가 어느 정도의 부하를 견딜 수 있는지 알아야 한다.&lt;/p&gt;
&lt;p data-end=&quot;932&quot; data-start=&quot;797&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어, 1초에 100명 요청도 못 버티는 서비스를 그냥 배포했다가 그 이상의 요청이 들어오면 서버는 바로 터질 것이다.&lt;/p&gt;
&lt;p data-end=&quot;932&quot; data-start=&quot;797&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그래서 &lt;b&gt;현재 서비스가 감당할 수 있는 트래픽 수준을 확인&lt;/b&gt;하고, &lt;b&gt;병목 구간을 찾아내 성능을 개선&lt;/b&gt;하는 과정이 필요하다.&lt;/p&gt;
&lt;p data-end=&quot;992&quot; data-start=&quot;934&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;992&quot; data-start=&quot;934&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;테스트 &amp;rarr; 병목 확인 &amp;rarr; 개선&lt;/b&gt;을 반복하면서 점점 안정된 서비스를 만들어가는 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-end=&quot;1015&quot; data-start=&quot;999&quot; data-ke-size=&quot;size26&quot;&gt;꼭 알아야하는 필수 개념 3가지&lt;/h2&gt;
&lt;h3 data-end=&quot;1041&quot; data-start=&quot;1016&quot; data-ke-size=&quot;size23&quot;&gt;1. Throughput (처리량)&lt;/h3&gt;
&lt;p data-end=&quot;1041&quot; data-start=&quot;1016&quot; data-ke-size=&quot;size16&quot;&gt;단위 시간(1초)당 시스템이 처리하는 트래픽 양&lt;/p&gt;
&lt;p data-end=&quot;1041&quot; data-start=&quot;1016&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1041&quot; data-start=&quot;1016&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TPS (Transactions Per Second)&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1041&quot; data-start=&quot;1016&quot;&gt;초당 처리되는 트랜잭션 수&lt;/li&gt;
&lt;li data-end=&quot;1041&quot; data-start=&quot;1016&quot;&gt;예: 1초에 100개 &amp;rarr; 100 TPS&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1041&quot; data-start=&quot;1016&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RPS (Requests Per Second)&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1041&quot; data-start=&quot;1016&quot;&gt;초당 처리되는 HTTP 요청 수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1229&quot; data-start=&quot;1205&quot; data-ke-size=&quot;size23&quot;&gt;2. Latency (지연 시간)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1261&quot; data-start=&quot;1230&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1261&quot; data-start=&quot;1230&quot;&gt;요청을 보낸 시점부터 응답을 받기까지 걸리는 시간&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1290&quot; data-start=&quot;1263&quot; data-ke-size=&quot;size23&quot;&gt;3. Availability (가용성)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1358&quot; data-start=&quot;1291&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1323&quot; data-start=&quot;1291&quot;&gt;시스템이 정상적으로 서비스를 제공할 수 있는 가능성&lt;/li&gt;
&lt;li data-end=&quot;1358&quot; data-start=&quot;1324&quot;&gt;장애 발생 확률이 높으면 가용성이 낮고, 적으면 높다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1377&quot; data-start=&quot;1360&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;가용성을 높이는 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1445&quot; data-start=&quot;1378&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1397&quot; data-start=&quot;1378&quot;&gt;서비스 장애 시간을 최대한 발생하지 않도록 한다.&lt;/li&gt;
&lt;li data-end=&quot;1419&quot; data-start=&quot;1398&quot;&gt;장애가 나더라도 빨리 복구한다. (장애 발생 시간이 짧도록)&lt;/li&gt;
&lt;li data-end=&quot;1445&quot; data-start=&quot;1420&quot;&gt;장애 시간을 줄이는 대표적인 방법 &amp;rarr; &lt;b&gt;시스템 이중화&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-message-model-slug=&quot;gpt-5&quot; data-message-id=&quot;dffe0c8b-a0b2-4a2f-8931-b22cb9f033c7&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;p data-end=&quot;1574&quot; data-start=&quot;1566&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시스템 이중화&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1574&quot; data-start=&quot;1566&quot; data-ke-size=&quot;size16&quot;&gt;시스템 일부에 장애가 발생하여도 다른 시스템을 이용할 수 있게 만드는 것&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1574&quot; data-start=&quot;1566&quot;&gt;ex) 일차선 도로에서 사고 발생 -&amp;gt; 도로 이용 불가&lt;/li&gt;
&lt;li data-end=&quot;1574&quot; data-start=&quot;1566&quot;&gt;일차선 도로를 이차선 도로로 확장 -&amp;gt; 일차선에서 사고가 발생해도 이차선 이용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1574&quot; data-start=&quot;1566&quot; data-ke-size=&quot;size23&quot;&gt;그 외 개념&lt;/h3&gt;
&lt;p data-end=&quot;1582&quot; data-start=&quot;1575&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;임계점&lt;/b&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;임계점.png&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;511&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/opqdq/btsQFt41ebS/TM0BHdIovn4ux7l5PCpV40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/opqdq/btsQFt41ebS/TM0BHdIovn4ux7l5PCpV40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/opqdq/btsQFt41ebS/TM0BHdIovn4ux7l5PCpV40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fopqdq%2FbtsQFt41ebS%2FTM0BHdIovn4ux7l5PCpV40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;832&quot; height=&quot;511&quot; data-filename=&quot;임계점.png&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;511&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-end=&quot;1657&quot; data-start=&quot;1583&quot; data-ke-size=&quot;size16&quot;&gt;TPS가 점점 올라가다가 &lt;b&gt;일정 수준에서 유지되는 지점이 임계점(병목 지점)&lt;/b&gt;이다.&lt;br /&gt;이 지점 이후부터는 요청이 밀리고, 응답 시간도 늘어난다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-end=&quot;1688&quot; data-start=&quot;1681&quot; data-ke-size=&quot;size26&quot;&gt;병목 지점&lt;/h2&gt;
&lt;p data-end=&quot;1719&quot; data-start=&quot;1689&quot; data-ke-size=&quot;size16&quot;&gt;특정 자원이 한계에 도달해서 성능이 떨어지는 구간이다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;병목지점.png&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7sSs8/btsQFNIYlYX/4K4GFwVx4PzFCYHQ5vICuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7sSs8/btsQFNIYlYX/4K4GFwVx4PzFCYHQ5vICuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7sSs8/btsQFNIYlYX/4K4GFwVx4PzFCYHQ5vICuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7sSs8%2FbtsQFNIYlYX%2F4K4GFwVx4PzFCYHQ5vICuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1456&quot; height=&quot;312&quot; data-filename=&quot;병목지점.png&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-end=&quot;1719&quot; data-start=&quot;1689&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1857&quot; data-start=&quot;1721&quot; data-ke-size=&quot;size16&quot;&gt;위 그림처럼 &lt;b&gt;b-c 구간에서 차선이 줄어들어 정체(병목 지점)&lt;/b&gt;가 발생한다면, 전체 처리량(Throughput)은 결국 b-c 구간 처리량에 맞춰진다.&lt;/p&gt;
&lt;p data-end=&quot;1857&quot; data-start=&quot;1721&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;따라서 성능 개선을 하고 싶다면 a-b 구간이 아니라 &lt;b&gt;b-c 구간을 개선해야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1857&quot; data-start=&quot;1721&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;즉,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;병목 지점을 찾아내고 그 부분을 개선하는 것&lt;/b&gt;이 성능 튜닝의 핵심이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1857&quot; data-start=&quot;1721&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1951&quot; data-start=&quot;1859&quot; data-ke-size=&quot;size16&quot;&gt;실제 시스템에서는 A=사용자, B=서버, C=데이터베이스라고 생각하면 된다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;2051&quot; data-start=&quot;2006&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;</description>
      <category>부하테스트</category>
      <author>s00jin</author>
      <guid isPermaLink="true">https://s00jin.tistory.com/43</guid>
      <comments>https://s00jin.tistory.com/43#entry43comment</comments>
      <pubDate>Thu, 18 Sep 2025 22:56:56 +0900</pubDate>
    </item>
    <item>
      <title>[동시성 문제] 더블 클릭과 네트워크 문제로 인한 동시성 문제 해결</title>
      <link>https://s00jin.tistory.com/42</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해커톤 운영진으로 활동하던 당시 굉장히 좋은 백엔드 멘토님을 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 백엔드 멘토님은 해커톤 진행 당시 밤을 새며 참가자들을 멘토링 해주셨는데, 쉬는 시간에도 쉬지 않고, 운영진들에게도 취업 멘토링을 해주셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 내 프로젝트와 포트폴리오도 멘토링 해주셨는데, 그때 해주신 질문이 스노우볼 효과로 여기까지 왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멘토님이 해주신 질문은 &lt;b&gt;이 프로젝트에 동시성 문제는 어떻게 해결했냐?&lt;/b&gt; 였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 자세하게 말하자면, 프로젝트 모집 공고에 동시에 지원하거나 동시에 수락하면 동시성 문제는 어떻게 되냐?였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 프로젝트 아래와 같은 특징이 있다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;단일 서버환경&lt;/b&gt;으로 구축&lt;/li&gt;
&lt;li&gt;프로젝트 모집 공고에 &lt;b&gt;지원자 수 제한이 없음&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;지원자 수락 또한 &lt;b&gt;모집 공고를 올린 사용자 단 한명만 가능&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 특징 때문에 동시성이 발생하지 않을 거라 생각했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 멘토님에게 내 생각과 함께 질문을 드렸다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;단일 서버 환경에서도 &lt;b&gt;더블클릭이나 네트워크 문제로 동시성 문제가 발생&lt;/b&gt;할 수 있어요&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각지도 못한 부분의 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더블클릭과 네트워크 문제로 인한 동시성 문제가 발생할거라곤 생각도 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;자바는 단일 서버 환경이라고 해서 단일 스레드 환경이 아니다.&lt;br /&gt;단일 서버에서도 멀티 스레도 환경으로 동작하기 때문에 위 같은 문제가 발생할 수 있던 것이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;동시성 문제 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시성 문제가 발생하는지 확인하기 위해 더미 데이터와 junit으로 멀티 쓰레드 환경을 만들어 테스트 해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;첫 시도&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정직하게 쓰레드를 2로 설정해서 테스트 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2로 설정하여 테스트 하려고 하니 문제 상황을 재현하기 힘들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;보완&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 스레드 수를 20으로 늘려 극단적인 상황을 시뮬레이션했다.&lt;/p&gt;
&lt;pre id=&quot;code_1758177577422&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootTest
public class DoubleClickTest {

    @Autowired
    private ApplicationService applicationService;

    @Autowired
    private FieldRepository fieldRepository;

    @Test
    void doubleClickTest throws InterruptedException {
        Long applicationId = 4001L; // userA 지원서
        Integer status = 2; // 2 = 지원서 수락

        int threadCount = 20; // 더블클릭 상황을 극단적으로 표현 &amp;rarr; 요청 20번
        ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
        CountDownLatch latch = new CountDownLatch(threadCount);

        for (int i = 0; i &amp;lt; threadCount; i++) {
            executorService.submit(() -&amp;gt; {
                try {
                    // 일부러 동시에 부딪히게 지연
                    Thread.sleep(100);
                    applicationService.acceptApplication(applicationId, status);
                } catch (Exception e) {
                    System.out.println(&quot;예외 발생: &quot; + e.getMessage());
                } finally {
                    latch.countDown();
                }
            });
        }

        latch.await();

        // 최종 Field 상태 확인
        // 1이 나와야 문제 없는 상황
        Field field = fieldRepository.findById(3000L).orElseThrow();
        System.out.println(&quot;최종 수락 인원(accept_member): &quot; + field.getAcceptMember() +
                &quot; / 정원(range_value): &quot; + field.getRange());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그랬더니 최종 수락 인원이 1도 아닌 20도 아닌 5가 나와버렸다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;동시성 문제 이미지.png&quot; data-origin-width=&quot;1570&quot; data-origin-height=&quot;564&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tYxM6/btsQFu3xbbA/AoCA4AnV8HIb8RSCLVkLk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tYxM6/btsQFu3xbbA/AoCA4AnV8HIb8RSCLVkLk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tYxM6/btsQFu3xbbA/AoCA4AnV8HIb8RSCLVkLk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtYxM6%2FbtsQFu3xbbA%2FAoCA4AnV8HIb8RSCLVkLk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1570&quot; height=&quot;564&quot; data-filename=&quot;동시성 문제 이미지.png&quot; data-origin-width=&quot;1570&quot; data-origin-height=&quot;564&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 후 기존 코드들을 다시 살펴봤다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 많아도 너무 많았다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 로직에서는 정원을 넘으면 모집 상태가 모집 종료로 변하면서 프론트 딴에서만 지원 화면에 접근할 수 없는 상태이다&lt;/li&gt;
&lt;li&gt;그래서 정상적인 사용자 흐름에서는 프론트 단에서 해결 가능하지만, 백엔드 관점에서는 문제가 될 수 있던거다.&lt;/li&gt;
&lt;li&gt;그리고 transactional 애너테이션이 누락되어 있었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 해결 1. 정원 초과 검증 로직 추가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정원 초과에 대한 검증을 백엔드에서 처리하지 않던 기존 코드이다.&lt;/p&gt;
&lt;pre id=&quot;code_1758177896364&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 필드 조회
        List&amp;lt;Field&amp;gt; fields = fieldRepository.findByProject_ProjectId(project.getProjectId());

        fields.forEach(field -&amp;gt; {
            // 수락되면 필드의 현재 수락된 멤버 수 증가
            if (field.getDepartment().equals(projectMember.getDepartment())) {    // 지원한 분야랑 같은 분야 확인
                field.setAcceptMember(field.getAcceptMember() + 1); // 같으면 지원 수락 된 인원 1 증가

                // 모집 인원보다 모집된 인원이 크거나 같으면 분야 모집 상태 2(모집 완료)로 수정
                if (field.getAcceptMember() &amp;gt;= field.getRange()) {
                    field.setDepartmentMemberStatus(2);
                }
            }
        });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 정원 초과 검증 로직을 추가한 코드이다.&lt;/p&gt;
&lt;pre id=&quot;code_1758177939722&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 필드 조회
        List&amp;lt;Field&amp;gt; fields = fieldRepository.findByProject_ProjectId(project.getProjectId());

        fields.forEach(field -&amp;gt; {
            // 수락되면 필드의 현재 수락된 멤버 수 증가
            if (field.getDepartment().equals(projectMember.getDepartment())) {    // 지원한 분야랑 같은 분야 확인
                // 정원 초과 검증 
                if (field.getAcceptMember() &amp;gt;= field.getRange()) {
                    throw new IllegalStateException(&quot;이미 정원이 가득 찼습니다.&quot;);
                }
                field.setAcceptMember(field.getAcceptMember() + 1); // 같으면 지원 수락 된 인원 1 증가

                // 모집 인원보다 모집된 인원이 크거나 같으면 분야 모집 상태 2(모집 완료)로 수정
                if (field.getAcceptMember() &amp;gt;= field.getRange()) {
                    field.setDepartmentMemberStatus(2);
                }
            }
        });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 해결 2. 동시성 문제 해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 동시성 문제를 어떻게 해결할까 고민해서 나온 세 가지 방안이 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;synchronized를 사용해서 동시성 문제 해결&lt;/li&gt;
&lt;li&gt;데이터 베이스 원자적 쿼리로 변경&lt;/li&gt;
&lt;li&gt;비관적 락 사용
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;데이터 수정 전 미리 해당 데이터에 접근 제한&lt;/li&gt;
&lt;li&gt;락으로 인한 성능 저하 발생 가능&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;낙관적 락 사용
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;트랜잭션 충돌이 발생하지 않을거라고 낙관적으로 가정&lt;/li&gt;
&lt;li&gt;여러 트랜잭션의 동시 접근 허용 &amp;rarr; 미리 락 X, 충돌이 발생하면 그때 처리&lt;/li&gt;
&lt;li&gt;@Version을 사용하여 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 1번으로 할까 했지만, synchronized는 단일 서버 환경에서만 보장이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 프로젝트는 추후 멀티 서버 환경을 구현할 수도 있는 프로젝트라 후보에서 제외했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 3, 4번 중 고민 후 비관적 락 방식을 선택했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 이유&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정원 초과 같은 실패하면 안되는 상황에서는 충돌 자체를 차단하는 게 맞다고 생각했다.&lt;/li&gt;
&lt;li&gt;낙관적 락은 재시도 로직까지 추가해야 해서 복잡도가 올라갈 것 같았다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비관적 락 적용&lt;/h3&gt;
&lt;pre id=&quot;code_1758178482725&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.kakaotrack.pin.project.repository;

import com.kakaotrack.pin.domain.Field;
import jakarta.persistence.LockModeType;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

public interface FieldRepository extends JpaRepository&amp;lt;Field, Long&amp;gt; {

    // ㅂㅣ관적 락 처리
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query(&quot;SELECT f FROM Field f WHERE f.project.projectId = :projectId&quot;)
    List&amp;lt;Field&amp;gt; findByProject_ProjectId(Long projectId);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비관적 락 처리를 위한 @Lock 과 @Query를 추가해줬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다시 위에서 똑같은 설정으로 테스트 해줬다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;잘못된 동시성.png&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WaHLB/btsQGgqaZ2c/i9mQwfgyONLhlOujMBYcg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WaHLB/btsQGgqaZ2c/i9mQwfgyONLhlOujMBYcg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WaHLB/btsQGgqaZ2c/i9mQwfgyONLhlOujMBYcg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWaHLB%2FbtsQGgqaZ2c%2Fi9mQwfgyONLhlOujMBYcg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;742&quot; height=&quot;223&quot; data-filename=&quot;잘못된 동시성.png&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 1이 나올 줄 알았지만, 0이 나왔다?!?!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 위에서 말한 @transactional 애너테이션 누락을 추가 안해줬다 ㅎㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1758178706658&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 지원서 수락
    @Transactional
    public void acceptApplication(Long applicationId, Integer status){
        // 지원서 조회
        Application application = applicationRepository.findById(applicationId)
                .orElseThrow(() -&amp;gt; new IllegalArgumentException(&quot;Application not found: &quot; + applicationId));

        // 지원서 상태 변경 (2 = 수락)
        application.setStatus(status);

        // DB 저장
        applicationRepository.save(application);

        // 프로젝트 멤버 테이블 추가
        addProjectMember(application.getAppProject(), application.getAppMember(), application.getDepartment());
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가해주고 다시 테스트 하니 동시성 문제가 해결되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;동시성 해결.png&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byAbAl/btsQGiVMRqZ/kYvdQCoaFwLkADb95kED0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byAbAl/btsQGiVMRqZ/kYvdQCoaFwLkADb95kED0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byAbAl/btsQGiVMRqZ/kYvdQCoaFwLkADb95kED0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyAbAl%2FbtsQGiVMRqZ%2FkYvdQCoaFwLkADb95kED0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;734&quot; height=&quot;256&quot; data-filename=&quot;동시성 해결.png&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;256&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>프로젝트/트러블슈팅</category>
      <author>s00jin</author>
      <guid isPermaLink="true">https://s00jin.tistory.com/42</guid>
      <comments>https://s00jin.tistory.com/42#entry42comment</comments>
      <pubDate>Thu, 18 Sep 2025 15:59:59 +0900</pubDate>
    </item>
  </channel>
</rss>