<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>kaori-killer.log</title>
        <link>https://velog.io/</link>
        <description>기술을 위한 기술이 되지 않도록!</description>
        <lastBuildDate>Fri, 19 Dec 2025 14:43:06 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>kaori-killer.log</title>
            <url>https://velog.velcdn.com/images/kaori-killer/profile/d57140db-a29b-4085-9d57-bfdb29f085b4/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. kaori-killer.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/kaori-killer" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[2025년 12월 매일매일 회고하기]]></title>
            <link>https://velog.io/@kaori-killer/2025%EB%85%84-12%EC%9B%94-%EB%A7%A4%EC%9D%BC%EB%A7%A4%EC%9D%BC-%ED%9A%8C%EA%B3%A0%ED%95%98%EA%B8%B0-%EC%9A%B0%ED%85%8C%EC%BD%94%EB%A5%BC-%EB%A7%88%EC%B9%98%EA%B3%A0-%ED%99%80%EB%A1%9C%EC%84%9C%EA%B8%B0</link>
            <guid>https://velog.io/@kaori-killer/2025%EB%85%84-12%EC%9B%94-%EB%A7%A4%EC%9D%BC%EB%A7%A4%EC%9D%BC-%ED%9A%8C%EA%B3%A0%ED%95%98%EA%B8%B0-%EC%9A%B0%ED%85%8C%EC%BD%94%EB%A5%BC-%EB%A7%88%EC%B9%98%EA%B3%A0-%ED%99%80%EB%A1%9C%EC%84%9C%EA%B8%B0</guid>
            <pubDate>Fri, 19 Dec 2025 14:43:06 GMT</pubDate>
            <description><![CDATA[<h3 id="12월-05일-금">12월 05일 (금)</h3>
<p><strong>K</strong></p>
<ul>
<li>면접을 꾸준히 다니는 중이다. 완벽한 준비는 없다고 생각하고 도전하고 있다.</li>
<li>면접 스터디도 매주하고 있다. 이력서 계속해서 피드백 받자.</li>
<li>너무 조급하지 않으려 한다. 우테코 다닐 때처럼일찍 일어나지 않지만, 이제부터 장기전이라 생각하고 천천히 루틴을 만드려고 한다.</li>
</ul>
<p><strong>P</strong></p>
<ul>
<li>면접에서 느낀 건데, 두괄식으로 말하기 어렵다. 두괄식으로 못말해도 깔끔하게 말해야 되는데,두 서없이 길게 말할 때가 있다.</li>
<li>또한 아직 자바스크립트, 리액트 기본 개념을 완전히 설명할 실력이 안되는 것 같다. 기초 탄탄히 하자.</li>
</ul>
<p><strong>T</strong>
팀 프로젝트 PR 리뷰 반영하기
앞으로 어떻게 공부할지 계획세우기</p>
<h3 id="12월-06일-토">12월 06일 (토)</h3>
<p>K</p>
<ul>
<li>처음으로 CLI 도구를 만들어봤다. 디자인시스템이나 코어팀에 관심이 있어서 이것저것 해보고 싶다.</li>
<li>오늘 지인의 음악 공연에 다녀왔다. 김광석 노래부르시는데 감동적이라 울뻔했다;; 내가 아쥼마 같다
P</li>
<li>문제는 CLI 도구가 다른 사람 컴에는 설치해도 동작을 안한다는 건데… 왜 안되는지 다시 봐야겠다.</li>
<li>스타트업 넣은 곳 결과가 내일로 밀렸다. 떨어져도 되니까 빨리 알려줬으면…
T</li>
<li>오전에 스터디 카페에서 작업하기 도전!</li>
</ul>
<h3 id="12월-07일-일">12월 07일 (일)</h3>
<p>K</p>
<ul>
<li>오랜만에 밖에서 데이트를 했다. 산책도 하고 연극도 보고 기분 전환이 돼서 좋았다. 날씨도 따뜻했다. 가끔은 밖에 나가서 데이트 해야겠다.</li>
<li>상대방의 노트북에서 npm 도구 실행이 안되는 이유를 알았다. 도구를 전역 설치하는 것과 전역 설치하지 않는 것이 실행 방식에 차이를 주는 것이었다. 이 부분에 대해서는 좀 더 공부를 해보려고 한다.</li>
</ul>
<p>P</p>
<ul>
<li>잠이 너무 안 와서 새벽 6시에 자버렸다. 오늘은 진짜 잘 버티다가 밤 11시에 자야겠다.
집에 며칠 동안 계속 혼자 있으니까 집중이 안됐다. 집중 안되면 춥더라도 나가야겠다.</li>
</ul>
<p>T</p>
<ul>
<li>밀린 일이 많은데 정리가 안된다. 내일은 카페가서 노트북만 하는 시간을 가져서 일을 정리해야겠다.</li>
</ul>
<h3 id="12월-08일-월">12월 08일 (월)</h3>
<p>K</p>
<ul>
<li>오늘은 일찍자고 일찍 일어났다.</li>
<li>면접 스터디도 잘했다. 공부한 부분에 대해서는 잘 말하는 것 같다. </li>
</ul>
<p>P</p>
<ul>
<li>오늘 몸이 좀 안 좋아서 밖에 못 나갔는데, 내일은 일정도 있으니 나가서 공부하자.</li>
<li>애니메이션 최적화 작업이 계속 밀리고 있다. 구현부가 복잡해서 어떻게 바꿔야 할 지 감이 안 잡힌다.
T</li>
<li>이제 우테코 팀플에서 벗어나 새로운 팀 프로젝트를 해보려고 한다. 내일 미팅 있는데 어떤 테스트를 어떻게 해볼지 구체적으로 정해야겠다.</li>
</ul>
<h3 id="12월-09일-화">12월 09일 (화)</h3>
<p><strong>K</strong></p>
<ul>
<li>당근 개발자와 커피챗을 했다. 디자인시스템 쪽에 관심이 있어서 방향성을 잡기 위해 했다. 예전부터 관심있는 분이라서 특별한 시간이었다. <ul>
<li>특별한 라이브러리를 만들어야 될 것 같았는데, 그런 것보다 바텀시트를 만들더라도 그 안에서 구조를 잘 잡는 거, 적절한 책임 분리 하는 게 중요하다고 느꼈다. 시중에 있는 라이브러리를 참고해서 많이 만들어봐야겠다. 양치기 </li>
<li>또 깨달은 것은 당근같은 기업은 완벽할 줄 알았는데, 생각보다 아직 부족한 점이 많다는 것. 예를 들어서 모바일 UX 지원이 미비한 점, 모든 기기에 대응하진 않는 점, 사용자 인터렉션이 카카오톡이나 토스에 비해 적은 점. 그리고 이런 부분들은 피그마로 디자인하는데 한계가 있어서 개발자가 디자인 영역까지 신경쓸 수도 있다는 점도. </li>
<li>토스와 당근 중에 당근을 선택하신 이유는 토스는 의사결정하는데 시간이 오래 걸려서 하고 싶은 일을 마음껏 할 수 없다고 한다. 토스가 인원이 많아지고 기업도 많아져서 그렇다고 했다. (신기했음)</li>
</ul>
</li>
<li>스타트업에 참여하기로 했다. 취준에 올인하지 않고 반반 어쩌면 스타트업에 더 몰입하기로 결정한 것이다. 100% 이 결정이 좋다고 할 순 없지만, 이게 더 재밌을 것 같아서 그냥 과감하게 선택했다. 좋아하는 걸 하기로.<ul>
<li>처음 받은 테스트는 SEO/AEO 기획이다. PM님과 이틀에 한번씩 회의하기로 했다. 개발이 아니라 기획으로 시작했지만 최근 FECONF 강남언니에서 발표했던 내용이라 관심을 가지고 있어서 흥미로웠다. 일단 SEO/AEO를 향상시키기 위해 무엇을 어느 범위까지 할 수 있을지 고민해야겠다.</li>
<li>다음 테스크는 회사 소개 페이지인데, 이때 NextJs를 사용하고 사용자 인터렉션이 있다고 해서 사전 공부가 필요할 것 같다. 언제 공부할지 고민 중이다.</li>
</ul>
</li>
<li>오늘 수영 기초반에서 초급반으로 승급 명령을 받았다. 두 달만에 기초반 탈출이다~ (사실 21살 때 수영을 배웠었다) 왼손이 알려준 의식적 학습을 적용하려고 노력했는데 수영 뿐만 아니라 인생 전반적으로 무언가를 학습학 때 빠르게 배울 수 있는 것 같다. 가장 좋은 습관인 된 것 같다.</li>
</ul>
<p><strong>P</strong></p>
<ul>
<li>커피챗 장소가 신논현이었는데 강남으로 가서 기다렸다. (ㄹㅈㄷ) 정말 죄송해서 죽는 줄 알았다. 나도 왜 머리로는 신논현이라고 생각하고 강남에 가 있었던 건지 모르겠다;;;</li>
</ul>
<p><strong>T</strong></p>
<ul>
<li>내일부터는 아침에 스카에 가서 테스트 작업을 하려고 한다. 시간 분배는 내일 해보고 결정해야겠다.</li>
<li>일찍자고 일찍 일어나기</li>
</ul>
<h3 id="12월-10일-화">12월 10일 (화)</h3>
<p><strong>K</strong></p>
<ul>
<li>오늘 처음으로 NextJs를 공부했다. 기회가 없어서 공부할 엄두를 못 냈는데,  NextJs 프로젝트를 곧 하게 될 것 같아서 벼락치기로 하고 있다. 기존에 React만 쓰던 CSR 방식과 얼마나 다른지 궁금하기도 하고 너무 어려울까봐 무섭기도 하고 ㅎㅎ</li>
<li>SEO/AO에 대해 공부했다. 인터넷 상에 SEO/AO를 지키기 위한 전략이 많았다. 그래서 더 우리 프로젝트에 맞는 전략을 세우기가 어려웠다. 사실 지금도 어렵다. 뭔가 쭉 나열해보긴 했는데 뭘 선택할지 고민이 된다. 내일 다시 한번 정리해야겠다.</li>
<li><em>P*</em></li>
<li>아침에 일찍 일어나서 스터디 카페에 갔는데, 엎드려서 1시간 반은 잔 것 같다 ㅋㅋ;;; 아직 아침 기상이 적응 안되나 보다.</li>
<li>공부하고 싶은 개념이 많은데 욕심이라는 걸 알아서 자중하는 중이다. 일단 지금은 당장 쓸 수 있는 NextJs, SEO에 집중하자. 꾸문 스터디를 하니까 계속 새로운 개념을 알게 돼서 더 그런 것 같다... 자제해야지.... 지금 하나 더 안다고 달라질 것 같진 않다.</li>
<li>다른 거 하다보니까 모잇지 애니메이션 작업 밀린다. PR에 올라온 CD도 리뷰하고 머지해줘야 하는데...!!!!! 내일 저녁에 시간 내서 해야겠다.</li>
<li><em>T*</em></li>
<li>선택과 집중하기.  (NextJs, SEO + UI 라이브러리)</li>
<li>작은 일이라도 오너쉽 가지고 하기.</li>
<li>결과물이 실패하더라도 최선을 다해서 했기에 아쉬움이 안 남도록 하기</li>
</ul>
<h3 id="12월-11일-화">12월 11일 (화)</h3>
<p><strong>K</strong></p>
<ul>
<li>오늘 SEO/AO 두번째 미팅을 했다. PM분이 비개발자시라 대화 도중에 내가 하는 개발 용어를 종종 못 알아들으셨다. (예. 시맨틱태그) 나도 최대한 쉽게 말하고 싶은데 아직은 그 경계를 모르겠다.<ul>
<li>그러면서 느낀 점은 하드스킬만큼 소프트스킬이 정말 중요한 것 같다. 핵심만 말하는 것, 깔끔하게 말하는 것, 말한 내용을 정리하는 것.</li>
<li>코드를 잘 짜면 사용자, 개발자의 시간을 아껴준다. 마찬가지로 대화도 잘해야 시간을 아낀다는 걸을 느꼈다. 시간은 곧 돈이니까, 어떻게든 아껴야 한다고 생각했다.</li>
</ul>
</li>
<li>1차 배포(12월 말)까지는 코드 수정 권한이 없어서, 이러다 설마 앞으로도 메인 스트림에 있는 코드에 참여 못하게 하는 거 아닌가 걱정했는데… 오늘 1차 배포 이후에 참여하게 해주겠다는 확실한 대답을 받았다. 다행이다. (아니면 나갑니다)</li>
<li>접영을 배웠다. 선생님이 잘한다고 칭찬해주셨다. 어릴 땐 이런저런 형편으로 피아노, 태권도, 수영 뭐하나 배워본 적이 없는데 오히려 다행이라는 생각이든다. 성인이 되니 배울게 많게 느껴지고 성인이 되니 더 빨리 배우는 것 같아서 좋다. 즐겁다.</li>
</ul>
<p><strong>P</strong></p>
<ul>
<li>돈이 없다. 알바를 해야할 정도이다. 통신비, 보험료, 식비, 교통비, CURSOR 비용 … 수입이 0이라서 충당할 돈이 없다. 어떻게 할지 고민이다. 일단 이번 달은 우테코 돈이 있긴한데…<ul>
<li>한 달에 얼마나 쓰는 지 파악하고, 재정 흐름을 제한해서 다른 곳으로 돈이 세지 않도록 해야겠다.</li>
<li>정 안되면 카페 알바의 길을 걷겠도다 :커피:</li>
</ul>
</li>
</ul>
<p><strong>T</strong></p>
<ul>
<li>말을 흐리지 않고 끝까지 하기 (힘있게 말하기)</li>
<li>핵심을 먼저 말하기 (헷갈리게 말하지 않기)<ul>
<li>우테코에서 맨날 반말하다가 이제 존댓말 하려니까 말이 안 나온다;;;</li>
</ul>
</li>
<li>NextJs 학습하기</li>
<li>재정 정리하기</li>
</ul>
<h3 id="12월-12일-화">12월 12일 (화)</h3>
<p><strong>K</strong></p>
<ul>
<li>오늘 하루종일 누워있었다. 누워서 넥스트JS 강의를 들었다. 42강을 들었고, 내일이면 완강할 수 있을 것 같다. 원래는 분할해서 들으려다가 맨날 강의 듣는 걸 미루는 나를 보며... 그냥 날을 잡아 각잡고 들어야겠다는 생각을 했고, 그게 오늘이 됐다. 다행히 많이 들었다.<ul>
<li>강의에서 Cursor를 적극적으로 이용하셨는데, 자동완성이 되니 흐름이 빨라 집중이 더 잘됐다. (예: CSS는 전부 Cursor 작성하게 하고 넘어갔다.)</li>
<li>넥스트JS 어려울까봐 걱정했는데 생각보다 쉬웠고 정말 편리한 도구였다. 서스팬스도 자동으로 지원해주고, 파일구조를 라우팅 구조로 가져가주고, 서버 쪽 코드도 작성할 수 있고, SSR과 CSR 방식 모두 지원하고. 재밌다.</li>
<li>이앤이 하고 동욱님이랑 커피챗 하기로 했다. 내가 약속 날짜를 번복하는 대역죄를 저질렀다 ㅎㅎ 당시에 죄를 빨리 저지르고 되는 날짜로 바꾸는 게 낫겠다고 판단했다. 그래도 괜찮게 마무리 된 것 같아서 다행이다.</li>
</ul>
</li>
</ul>
<p><strong>P</strong></p>
<ul>
<li>아직 어떤 상황에 NextJS를 쓰면 좋고, 안쓰면 좋은지는 모르겠다.</li>
<li>오늘 하루종일 누워있었더니 살찌는 기분이다.</li>
<li>가끔 누워만 있으면 과거의 이불킥 하는 순간들이 떠오른다. 언제쯤 잊을지 모르겠다.</li>
<li>PM 님께 DM 드렸는데 반응이 없으시다. 무시한건 아니고 까먹으신 것 같은데 ... 중요한 내용은 아니라서 굳이 다시 보내진 않으려고 한다. 요즘 드는 생각은 &#39;해야할 말을 해야할 때&#39; 하고 싶다는 것인데, 이런 일도 그 경계에 포함되나? 싶다.</li>
</ul>
<p><strong>T</strong></p>
<ul>
<li>넥스트JS 강의 마무리하기</li>
<li>시지프가 추천해준 &#39;결정적 순간의 대화&#39; 책 구매하기</li>
<li>유튜브로 대화하는 스킬 관련 영상 찾아보기</li>
</ul>
<h3 id="12월-13일-화">12월 13일 (화)</h3>
<p><strong>K</strong></p>
<ul>
<li>해외 취업 컨퍼런스에 다녀왔다. 느낀 점은 신입은 해외도 취업이 어렵다. 국내 취업도 안되는데 해외 취업이 될 리 없다는 뜻이다. 또한 비자 문제도 있다. 캐나다를 제외하고는 워홀 비자로 일할 수 있는 곳이 없다.<ul>
<li>그래서 전략을 정했다. 1. 내년에 DH 지원 / 2. 경력직 이직</li>
<li>그 전까지 영어공부 / 영문 이력서 / 코딩테스트 를 준비해보려 한다.</li>
</ul>
</li>
<li>NextJS 강의를 완강했다. 이제 NextJS로 어떻게 프로젝트를 만들지 구상해봐야겠다. (아직은 계획없음)</li>
</ul>
<p><strong>P</strong></p>
<ul>
<li>다음 테스크를 위해 공부가 필요한 부분이<ul>
<li>Shadcn, 에러바운더리, 시멘틱태그, SEO 가 있는데...</li>
<li>각각이 내용이 깊다보니 뭐부터 해야하나 싶긴하다.</li>
<li>일단 금방 학습할 수 있는 것부터 해야겠다.</li>
</ul>
</li>
<li>무급으로 일하니까 약간의 불안감이 어쩔 수 없이 있다. 언제 해고(?)를 당할지 모른다는 불안감. 무급인데도 계약서를 안 쓰니까 불안하다 ㅋㅋㅋ 그래도 얻을 수 있을만큼 얻고, 회사랑은 좋은 관계를 맺을 수 있도록 노력해야겠다. 물론 유저에게도 좋은 임펙트를 주고 떠나고 싶다.</li>
</ul>
<p><strong>T</strong>
토스 지원하기 (얘들아 내일까지야...~)</p>
<h3 id="12월-14일-화">12월 14일 (화)</h3>
<p><strong>K</strong></p>
<ul>
<li>북한산 등산을 했다. 거의 6시간이 걸렸다. 산이 너무 가파라서 진짜 죽을 뻔 했다. 눈 오는 산에 아이젠 없이 가면 안될 것 같다. 정말 친절하신 분들이 산에 많아서, 아이젠도 빌려주시고 먹을 것도 나눠주시고, 사진도 찍어주셨다. 아직 세상은 살만 하다. 오랜만에 운동하니까 좋았다. (정상 부근까지 올라갔을 땐 너무 힘들어서 계속 욕했다)<ul>
<li>외국인 친구랑 같이 했다. 오랜만에 영어공부 해서 좋았다. 자랑스러운 한국의 산을 보여줄 수 있어서 좋았다.</li>
</ul>
</li>
</ul>
<p><strong>P</strong></p>
<ul>
<li>토스뱅크 제출했는데, 이력서가 맘에 들지 않는다. 아직 내 강점을 모르겠다. 문제의 원인을 분석하고 파고드는 것이라고 생각했는데 그렇지 않은 사람이 없는 것 같다. 또 도전정신이 있긴 한데 이력서 하위에 그걸 증명하기 어렵다. 나를 어떻게 드러내면 좋을까.</li>
</ul>
<p><strong>T</strong></p>
<ul>
<li>화요일 SEO 미팅 자료 준비하기</li>
</ul>
<h3 id="12월-15일-화">12월 15일 (화)</h3>
<p><strong>K</strong></p>
<ul>
<li>청계천에서 잉어킹을 봤다. 밖에 나와서 산책하니까 기분이 좋다. 사소한 행복.
목도리를 선물 받았다. 호주가서 도둑맞은 목도리와 같은 목도리였다. 그 따뜻함이 그리웠는데 다시 갖게 돼서 좋았다.</li>
</ul>
<p><strong>P</strong></p>
<ul>
<li>면접 스터디를 나갈까 고민 중이다. 기술에 대해 공부를 하는 것은 좋은데 몇 가지 안 맞는 점이 있다.<ul>
<li>스터디 준비할 시간이 부족하다. 한 주에 최소 4개 최대 6개의 개념을 학습해야 한다. 그러면 하루에 한 개씩은 해야 하는데, 불가능했다. 스타트업 일을 처리하는 것만으로도 하루가 바빴다.
다들 스터디 준비를 잘 안해온다. 위와 같은 이유로 다들 준비를 안해온다. 오늘은 모두가 정답을 몰라서 추측만 하다가 끝났다.</li>
<li>스터디 방식이 바뀌더라도, 잘 안 맞을 것 같다. 나는 하나의 개념을 깊이 공부하는 걸 좋아하는데, 이   - 스터디 자체가 여러 개념을 얕게 학습하는 방향으로 흘러간다. 스터디 방향을 바꾸긴 쉽지 않을 것 같다.
그래서 그만둘까 고민이다.</li>
<li>하지만 스터디원들에게 미안해서 어떻게 하지… 고민하는 중 :울음:</li>
</ul>
</li>
</ul>
<p><strong>T</strong></p>
<ul>
<li>면접 스터디 앞으로 할 지 말 지 진지하게 고민해보기 (여러분 조언 부탁해요..)</li>
<li>내일 미팅 자료 준비하기</li>
</ul>
<h3 id="12월-16일-화">12월 16일 (화)</h3>
<p><strong>K</strong></p>
<ul>
<li>SEO/AO 3번째 미팅을 했다. 이제 좀 SEO 향상을 위해 무엇을 해볼 수 있는지 그림이 그려진다. 몇 가지 시도해보고 실제 유저 유입까지 이뤄지는지 확인할 예정이다. 개발도 좋지만 이런 기획도 좋은 것 같다. SEO 기술을 학습하는 것을 너머 “어떻게 하면 사람들을 많이 유입시키지?”라는 더 넓은 관점의 고민을 하게되는 요즘이다.</li>
<li>우테코 핑계로 설거지를 안했었는데 (불효년) 오늘 엄마가 힘드신지 설거지를 안하시고 잠드셨다. 그래서 100년만에 내가 설거지를 했다. 이렇게 간단하고 쉬운 걸 안했던 나를 반성했다.</li>
<li>오늘 수영 수업이 끝나고, 가장 못하시지만 열정이 넘치시는 아주머니 한 분에게 수영을 알려드렸다. 살면서 누군가를 가르쳐 본 적이 없는데 짧은 시간이었지만 뭔가 설레었다(?) 평소에는 ‘나’만 컨트롤 하면 됐늗데, 가르치면서 ‘남’을 컨트롤 하려니까 ‘왜’ 안되지? 라는 생각과 함께 제 3자 입장에서 더 많은 가설을 세우게 됐던 것 같다. 그리고 잘 가르치고 싶다는 약간의 의욕도 생겼었다 ㅋㅋ 나중에 가르치는 사람이 돼도 좋겠다는.. 짧은 생각을 했다.</li>
</ul>
<p><strong>P</strong></p>
<ul>
<li>PM님이 이런 질문을 하셨다. “검색엔진은 어떤 기준을 가지고 우리 페이지를 노출 시키는 거예요? 다른 페이지보다 상단에 노출하려면 어떻게 해야 돼요?” 답은 알고 있었는데 대답하기 어려웠다.<ul>
<li>첫 번째로, SEO 관점에서 할 수 있는게 무수히 많아서 무엇부터 얘기하면 좋을지 모르겠었다.</li>
<li>두 번째로, 일단 하나씩 얘기해봤는데 기술적인 용어를 안쓰고 말하는 게 어려웠다. (예. 시멘틱 태그, LCP, 링크 간의 연결성)</li>
<li>세 번째로, 나도 SEO 전문가가 아니라서… 그리고 SEO는 뭔가를 한다고 노출이 빡 되는 게 아니라서, 100% 확신을 가지고 말하기 어려웠다.
네 번째로, robots 속성을 왜 쓰냐고 물어보썼는데 (내가 작성한 스크립트인데도) 어떤 효용이 있는지 기억이 나지 않는 참사가 있었다. (그냥 솔직하게 기억이 나지 않는다고, 찾아보겠다고 말씀드릴 걸 그랬다.)
=&gt; 앞으로는 노트에 정리하면서 말을 하는 연습을 해야겠다. 도식화를 해서 말해야겠다. 그럼 좀 더 차분히 순차적으로 말할 수 있을 것 같다.</li>
</ul>
</li>
<li>토스뱅스 과제 전형이 잡히면서 친구와의 약속을 깼다. 가까운 사람일 수록 더 약속을 잘 지켜야 하는데 미안한 마음이 크다… :부탁하는_얼굴:</li>
</ul>
<p><strong>T</strong></p>
<ul>
<li>매일 매일 해야 할 일에 집중하고 해내기</li>
<li>일주일에 1개의 회사에 지원하기</li>
<li>차분히 도식화에서 설명하는 연습하기</li>
</ul>
<h3 id="12월-17일-화">12월 17일 (화)</h3>
<p><strong>K</strong></p>
<ul>
<li>SEO 관련해서 JSON 스키마를 작성 중인데, 좋은 공식 문서를 발견해서 좋았다. 구글이 작성한 것이었다. 원래 처음에는 AI한테 작성을 시켰다가  어떤 기준을 작성해서 주는지 모르겠어서, 네이버 공식문서를 보면서 다시 작성했는데 너무 AI 준거랑 달라서 고민이 많았다. 다행히도 구글이 작성한 건 AI랑 비슷하고 또 내용이 자세해서 앞으로 이걸 보면 되겠다는 생각을 했다.</li>
<li>미래 이력서를 작성했다. 앞으로 할 일과 결과를 예상해서 이력서에 적었다. 우테코 프로젝트에서는 빌드 최적화를 해볼 예정이고, 스타트업에서는 사용자를 활용해서 SEO, GA, 에러처리 작업에 도전해볼 생각이다. 잘 될지는 모르겠다. 원래는 우테코 프로젝트 작업에서 전부 해보고 싶었던 것들인데, 우리 프로젝트가 완전 멈춰버려서 할 수 없게 됐다. 다들 취준기간이라 바쁜 것이다...</li>
</ul>
<p><strong>P</strong></p>
<ul>
<li>갑자기 사극에 꽂혀서 하루종일 봐버렸다. 시간낭비를 너무 많이 했다. 늘 하나에 꽂히면 결말까지 봐야하는 성격이 일을 미루게 만든다. 완결이 나온 드라마를 보는 건 자제해야겠다.</li>
</ul>
<p><strong>T</strong></p>
<ul>
<li>GA 공부하기</li>
<li>내일 미팅 준비하기</li>
</ul>
<h3 id="12월-18일-화">12월 18일 (화)</h3>
<p><strong>K</strong></p>
<ul>
<li>오늘은 AEO/GA에 관한 미팅을 했다. 사전에 잘 준비해가서 시간낭비 안하고 마무리할 수 있었다. 12월 말쯤에 내가 직접 코드를 넣게 해주신다니 설렌다. 그만큼 책임도 따라오니 잘 해야겠지! 주어진 테스트를 잘 해내서 책임감을 성과로 증명하자!</li>
<li>오늘 수영에서 같은 반 이모님 수영을 도와드렸는데, 씻고 나갈 때 갑자기 뛰어오셔서 우유 한 팩을 주셨다. 누군가를 선한 마음으로 도와준다는 것은 이렇게 보답을 받을 수 있는 일이구나 싶어서 마음이 따뜻했다.</li>
</ul>
<p><strong>P</strong></p>
<ul>
<li>대표님과 짧은 인사를 나눴다. 궁금한 점 있냐고 물어보셨는데 (진심으로) 딱히 없어서 질문하기 어려웠다. 앞으로 어려운 분과의 만남 자리에서는 미리 질문을 준비해야겠다.<ul>
<li>&quot;사우나 얼마나 좋아해요?&quot; 라고 질문도 해주셨는데, 너무 이 질문이 부담스러워서 &quot;아ㅏ.. 안 좋아합니다&quot; 라고 해버렸다. 사실 엄청 좋아하는데 척도로 표현할 수 있는 부분이 아니라서 회피했다. 앞으로 이 질문을 꽤 받을 것 같으니 ... 그냥 일주일에 한 두 번 갈 정도로 좋아한다고 해야겠다.</li>
<li>3월 복학할 때까지 뭐하냐고 물어보셔서, 취준한다고 했다. 근데 취준한다고 괜히 말했나 싶다. (괜스레 후회) 여기 다니면서 다른 회사 준비한다고 한 것 같아서 좀 그랬다... 근데 여기 무급인데 취준해야지 뭐 어떻게 해요 ㅜㅜ 나도 돈은 벌어야지 :울음: 돈 벌어야지 생활비 낼 수 있단 말이에요 ㅜㅜ =&gt; 앞으로는 너무 솔직하지 말자... 적당히 솔직하게 문제 없는 선에서 말하자.</li>
</ul>
</li>
<li>회사에서 Slack 으로 주로 소통을 하는데, 나는 DM을 잘 이용했다. 근데 오늘 깨달았다. DM은 별로 좋지 않은 통신 수단이라는 것을... 회사에서 중요한 것 중 한 가지는 &quot;저 정말 열심히 일하고 있습니다&quot; 라고 티내는 것이다. 개인적인 이야기가 아니라면 전체 채널을 통해서 소통하는 것이 나를 홍보하는 방법이었다. 쓸데없이 개인 DM으로 소통해서 겸손하려고 하지 말자. =&gt; 이런 걸 느끼는 것을 보면 나도 사회 첫 걸음을 뗀 기분이다.</li>
</ul>
<p><strong>T</strong></p>
<ul>
<li>GA 기능을 분석하고 어떤 코드를 넣을 지 구상하기</li>
<li>말을 주도적 할 수 있도록 의식적 학습하기</li>
</ul>
<h3 id="12월-19일-화">12월 19일 (화)</h3>
<p><strong>K</strong></p>
<ul>
<li>동욱님과 커피챗을 했다. 다이앤이 함께해줘서 용기를 가지고 신청했다. 너무 좋고 많은 인사이트를 얻었다. 그래서 인상 깊었던 딱 3가지만 소개하겠다.<ul>
<li><ol>
<li>신입 이력서를 볼 때 &quot;Github&quot; 와 &quot;Blog&quot; 를 먼저 확인한다는 점이다. 나도 이력서로 나를 어떻게 판단하시지 했는데, 역시 코드와 글을 먼저 확인하신다고 한다. 앞으로 Github에 &quot;코드&quot;를 잘 정제해서 올리고, 블로그에 &quot;내 생각&quot;을 많이 적어야겠다. </li>
</ol>
</li>
<li><ol start="2">
<li>Why를 정말 중요하게 생각한다는 점이다. 그래도 나는 Why에 대해 잘 고민하는 편이라고 생각했는데 아니었다. 동욱님 하시는 얘기 들어오면 &quot;더 깊게&quot; 고민해야 할 듯 싶다. 예를 들어, 하나의 작업을 해도 &quot;이 작업의 배경이 뭐자?&quot;, &quot;왜 이 방법을 선택했지?&quot;, &quot;결과는 어떻지?&quot;, &quot;비지니스 임펙트와 어떻게 연결되지?&quot; 전체적인 관점에 대한 근본적인 이유를 알고 있어야했다. 이번에 일하면서 이걸 의식적으로 연습해야겠다.</li>
</ol>
</li>
<li><ol start="3">
<li>설계를 굉장히 중요하게 생각하신다는 점이다. 좋은 설계, 더 쉽게 말하자면 좋은 코드를 짜는 건 한 순간에 되는 일이 아니다. 계속해서 고민해서 추상화, 인터페이스를 어떻게 가져갈지 학습해야겠다.</li>
</ol>
</li>
</ul>
</li>
</ul>
<p><strong>P</strong></p>
<ul>
<li>토스에 방문하고 너무 설레서 인증 사진까지 찍었다 :kk:<ul>
<li>너무 학생처럼 보였나 ㅎㅎㅎㅎ</li>
</ul>
</li>
</ul>
<p><strong>T</strong></p>
<ul>
<li>일본 여행가기 전에 GA 문서 정리해서 공유하기</li>
<li>디자인시스템 라이브러리 간단하게 학습하기</li>
<li>블로그에 회고글 정리해서 올리기</li>
<li>토스 과제 치를 준비하기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[포트 관리, 이제 편하게 해결하고 개발에 집중하세요]]></title>
            <link>https://velog.io/@kaori-killer/%ED%8F%AC%ED%8A%B8-%EA%B4%80%EB%A6%AC-%EC%9D%B4%EC%A0%9C-%ED%8E%B8%ED%95%98%EA%B2%8C-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B3%A0-%EA%B0%9C%EB%B0%9C%EC%97%90-%EC%A7%91%EC%A4%91%ED%95%98%EC%84%B8%EC%9A%94</link>
            <guid>https://velog.io/@kaori-killer/%ED%8F%AC%ED%8A%B8-%EA%B4%80%EB%A6%AC-%EC%9D%B4%EC%A0%9C-%ED%8E%B8%ED%95%98%EA%B2%8C-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B3%A0-%EA%B0%9C%EB%B0%9C%EC%97%90-%EC%A7%91%EC%A4%91%ED%95%98%EC%84%B8%EC%9A%94</guid>
            <pubDate>Sat, 06 Dec 2025 16:06:21 GMT</pubDate>
            <description><![CDATA[<h1 id="🚨-error-port-3000-is-already-in-use">🚨 &quot;Error: Port 3000 is already in use&quot;</h1>
<p>개발하다 보면 정말 자주 마주치는 에러입니다. 😮‍💨</p>
<p>예전엔 이렇게 해결했습니다.</p>
<pre><code class="language-bash">lsof -i
lsof -ti :3000 | xargs kill</code></pre>
<p>요즘엔 LLM에게 요청합니다. </p>
<blockquote>
<p>&quot;포트 3000 죽여줘&quot;</p>
</blockquote>
<p>*<em>하지만 이제는?
*</em></p>
<pre><code class="language-bash">port kill 3000</code></pre>
<p>딱 이것만 입력하면 됩니다.</p>
<hr>
<h1 id="📦-kaori-killerport를-소개합니다">📦 @kaori-killer/port를 소개합니다</h1>
<p>복잡한 명령어는 이제 그만!!! 🙅</p>
<p>간단하고 직관적으로 포트 관리를 할 수 있는 CLI 도구를 소개합니다.</p>
<h2 id="1️⃣-설치">1️⃣ 설치</h2>
<pre><code class="language-bash">npm install -g @kaori-killer/port</code></pre>
<h2 id="2️⃣-사용법">2️⃣ 사용법</h2>
<pre><code class="language-bash">port ls                 # 실행 중인 모든 포트 확인
port check &lt;포트 번호&gt;  # 특정 포트 상태 확인
port kill &lt;포트 번호&gt;   # 특정 포트 종료</code></pre>
<h2 id="직접-명령어-vs-port">직접 명령어 vs port</h2>
<table>
<thead>
<tr>
<th>작업</th>
<th>직업 명령어</th>
<th>port</th>
</tr>
</thead>
<tbody><tr>
<td>포트 목록</td>
<td><code>lsof -i -P -n | grep LISTEN</code></td>
<td><code>port ls</code></td>
</tr>
<tr>
<td>포트 확인</td>
<td><code>lsof -i :3000 -P -n</code></td>
<td><code>port check 3000</code></td>
</tr>
<tr>
<td>프로세스 종료</td>
<td><code>lsof -ti :3000 | xargs kill</code></td>
<td><code>port kill 3000</code></td>
</tr>
</tbody></table>
<h2 id="주요-장점">주요 장점</h2>
<ol>
<li><strong>간단한 명령어</strong>: 복잡한 <code>lsof</code> 명령어 대신 직관적인 <code>port</code> 명령어를 사용합니다.</li>
<li><strong>일관된 구조</strong>: 모든 작업이 <code>port &lt;action&gt;</code> 형식으로 통일됩니다.</li>
<li><strong>친절한 에러 처리</strong>: 프로세스가 없을 때 명확한 안내 메시지가 출력됩니다.</li>
<li><strong>종료 확인</strong>: 프로세스 종료 후 자동으로 확인 메시지가 표시됩니다.</li>
</ol>
<hr>
<h1 id="🍵-마무리하며">🍵 마무리하며...</h1>
<p>매일 반복되는 작업을 단순화해서 생산성을 높여봤습니다.
아직 ls, check, kill 명령어만 지원하지만 필요에 따라 더 확장할 예정입니다.
많은 사람들이 <code>port</code> 명령어 하나로 포트 관리를 손쉽게 했으면 좋겠습니다.
그래서 시간을 더 아껴서 개발에 집중하셨으면 좋겠습니다.</p>
<p>더 자세한 내용은 <a href="https://github.com/kaori-killer/port/blob/main/README.ko.md">깃허브 링크</a>를 참고해주세요.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 최적화 기법에 관심이 있는 당신, JSX와 VDOM에 대해서는 아시나요?]]></title>
            <link>https://velog.io/@kaori-killer/React-%EC%B5%9C%EC%A0%81%ED%99%94-%EA%B8%B0%EB%B2%95%EC%97%90-%EA%B4%80%EC%8B%AC%EC%9D%B4-%EC%9E%88%EB%8A%94-%EB%8B%B9%EC%8B%A0-JSX%EC%99%80-VDOM%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C%EB%8A%94-%EC%95%84%EC%8B%9C%EB%82%98%EC%9A%94</link>
            <guid>https://velog.io/@kaori-killer/React-%EC%B5%9C%EC%A0%81%ED%99%94-%EA%B8%B0%EB%B2%95%EC%97%90-%EA%B4%80%EC%8B%AC%EC%9D%B4-%EC%9E%88%EB%8A%94-%EB%8B%B9%EC%8B%A0-JSX%EC%99%80-VDOM%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C%EB%8A%94-%EC%95%84%EC%8B%9C%EB%82%98%EC%9A%94</guid>
            <pubDate>Mon, 24 Nov 2025 07:48:59 GMT</pubDate>
            <description><![CDATA[<p>React를 사용한다면 다음과 같이 JSX를 사용해봤을 것입니다.</p>
<pre><code class="language-tsx">const element = &lt;h1&gt;Hello, world!&lt;/h1&gt;;</code></pre>
<p>JSX로 인해 그동안 편하게 코드를 작성했었죠?
먄약 JSX의 어떤 점이 편했는지 모르겠다면, 이 글을 한 번 읽어보세요! 😊</p>
<p>이 글에서는 JSX와 VDOM에 대해 설명하고 있습니다.
이는 React 최적화 기법을 공부하기 위해서 꼭 알아야 할 지식입니다.</p>
<h2 id="🙋-jsx가-뭐예요">🙋‍ JSX가 뭐예요?</h2>
<p>JSX는 XML-like syntax extension to ECMAScript의 약자로, 자바스크립트 확장한 문법입니다. JSX의 형태는 XML과 유사합니다. 예를 들어, <code>&lt;name&gt;&lt;/name&gt;</code> 이런 형태를 가질 수 있습니다. 위에서 예시로 보여줬던 <code>&lt;h1&gt;&lt;h1&gt;</code> 도 마찬가지입니다. 생김새가 XML과 유사하죠?</p>
<h2 id="🙋-jsx는-react에서만-써야-돼요">🙋‍ JSX는 React에서만 써야 돼요?</h2>
<p>아니요. JSX는 React에서 쓰지 않아도 됩니다. Vue 같은 라이브러리에서도 사용할 수 있습니다. React의 부산물로 JSX가 탄생했기 때문에 사람들이 종종 이런 오해를 합니다.</p>
<h2 id="🙋-jsx는-쓰는-건-필수예요">🙋‍ JSX는 쓰는 건 필수예요?</h2>
<p>아니요. JSX는 필수가 아닙니다. JSX는 자바스크립트 코드와 1:1로 매칭 됩니다. 그럼 JSX를 쓰지 않고 자바스크립트 코드를 써도 된다는 말이 돼죠.</p>
<h2 id="🙋-그럼-jsx는-왜-쓰는-거예요-그냥-자바스크립트-코드를-써도-되지-않아요">🙋‍ 그럼 JSX는 왜 쓰는 거예요? 그냥 자바스크립트 코드를 써도 되지 않아요?</h2>
<p>맞습니다. 자바스크립트 코드를 써도 됩니다. 하지만 JSX를 쓰는 이유는 편해서입니다. JSX코드와 JSX코드를 자바스크립트로 변환한 코드를 비교해보겠습니다.</p>
<p>해당 코드는 Increase라고 적힌 버튼을 클릭했을 때, count 값이 증가하는 증가하는 코드입니다. 한 눈에 봐도 JSX코드가 자바스크립트 코드보다 가독성이 좋고 짧지 않나요? 그래서 JSX를 쓰는 것입니다.</p>
<p><strong>JSX 코드</strong></p>
<pre><code class="language-tsx">&lt;div&gt;
    &lt;p&gt;Count: {count}!&lt;/p&gt;
    &lt;button type=&quot;button&quot; onClick={() =&gt; setCount(count + 1)}&gt;Increase&lt;/button&gt;
&lt;/div&gt;</code></pre>
<p><strong>변환된 JS 코드</strong></p>
<pre><code class="language-tsx">React.createElement(
    &quot;div&quot;,
    null,
    React.createElement(&quot;p&quot;, null, &quot;Count: &quot;, count, &quot;!&quot;),
    React.createElement(&quot;button&quot;, { type: &quot;button&quot;, onClick: () =&gt; setCount(count + 1) }, &quot;Increase&quot;)
);</code></pre>
<h2 id="🙋jsx를-어떻게-인식하는-거예요">🙋‍JSX를 어떻게 인식하는 거예요?</h2>
<p>컴파일은 JSX가 아닌 자바스크립트만 가능합니다. 그래서 JSX의 XML처럼 작성된 부분분은 자바스크립트 코드로 변환이 돼야 하는데, 이때 Babel이나 Parcel과 같은 변환기를 통해서 자바스크립트로 변환이 됩니다. 이는 React.createElement를 쓰는 자바스크립트 코드로 변환 됩니다.</p>
<p><strong>JSX 코드</strong></p>
<pre><code class="language-tsx">&lt;p&gt;Hello, World&lt;/p&gt;</code></pre>
<p><strong>자바스크립트로 변환한 코드</strong></p>
<pre><code class="language-tsx">React.creteElement(&quot;p&quot;, null. &quot;Hello, world!&quot;);</code></pre>
<h2 id="🙋-jsx는-reactcreateelement를-쓰는-자바스크립트-코드로-변환-되는-군요-reactcreateelement는-어떤-구조를-가져요">🙋‍ JSX는 React.createElement를 쓰는 자바스크립트 코드로 변환 되는 군요! React.createElement는 어떤 구조를 가져요?</h2>
<p>React.createElement는 다음과 같은 구조를 가집니다. React.createElement(함수, 속성, 자녀) 로 이해하면 됩니다.</p>
<p><strong>React.createElement 구조</strong></p>
<pre><code class="language-tsx">React.createElement(
    type,         // 함수
    [props],      // 속성
    [...children] // 자녀
)</code></pre>
<p>JSX 코드랑 비교해서 보면 더 쉽게 이해할 수 있습니다.</p>
<p><strong>JSX 코드</strong></p>
<pre><code class="language-tsx">JSX 코드

&lt;div&gt;
    &lt;p&gt;Count: {count}!&lt;/p&gt;
    &lt;button type=&quot;button&quot; onClick={() =&gt; setCount(count + 1)}&gt;Increase&lt;/button&gt;
&lt;/div&gt;

// 참고로 자녀는 React.Fragment나 div 같은 태그를 이용해서 바깥에서 감싸줘야 하는데, 
// 각 줄을 구분하기 위해서입니다.</code></pre>
<p><strong>변환된 JS 코드</strong></p>
<pre><code class="language-tsx">React.createElement(
    &quot;div&quot;,
    null,
    React.createElement(&quot;p&quot;, null, &quot;Count: &quot;, count, &quot;!&quot;),
    React.createElement(&quot;button&quot;, { type: &quot;button&quot;, onClick: () =&gt; setCount(count + 1) }, &quot;Increase&quot;)
);
</code></pre>
<p>코드를 자세히 보면서 특징을 파악해봅시다.</p>
<table>
<thead>
<tr>
<th>변환된 JS 코드의 특징</th>
<th>변환된 JS 코드에서 예시</th>
</tr>
</thead>
<tbody><tr>
<td>속성과 자녀는 값이 없다면 null값이 들어간다.</td>
<td>div태그의 속성이 없어서 null값이 들어갔다.</td>
</tr>
<tr>
<td>자녀는 여러 명일 수 있다. 세 번째 인자부터 자녀의 수만큼 열거 된다.</td>
<td>div태그의 자녀로 p태그와 button태그가 있다. 이는 세 번째 인자부터 열거됐다.</td>
</tr>
<tr>
<td>type을 소문자로 쓰면 HTML 태그로 인식하고, 대문자로 쓰면 Component로 인식한다.</td>
<td>div를 소문자로 썼기 때문에 HTML 태그로 인식한다.</td>
</tr>
</tbody></table>
<h2 id="🙋-치환된-reactcreateelement는-무엇을-만드는-코드예요">🙋‍ 치환된 React.CreateElement는 무엇을 만드는 코드예요?</h2>
<p>React.createElement는 VDOM 트리의 React Element를 만드는 코드입니다.</p>
<h2 id="🙋-react-element가-뭐예요">🙋‍ React Element가 뭐예요?</h2>
<p>먼저 Element에 대해 설명해드리겠습니다. Element는 트리의 노드입니다. 예를 들어, <code>document.createElement(’div’)</code> 코드는 트리의 노드를 만듭니다. <code>&lt;div&gt;&lt;/div&gt;</code> 라는 DOM 트리의 노드를 만드는 것입니다. 이걸 DOM 트리의 Element라고도 합니다. 이처럼 React.createElement 코드도 트리의 노드를 만듭니다. VDOM 트리의 노드를 만드는 것입니다. 이를 VDOM 트리의 React Element라고 합니다.</p>
<h2 id="🙋-vdom-트리는-뭐예요">🙋‍ VDOM 트리는 뭐예요?</h2>
<p>VDOM은 Virtual DOM입니다.. 이름 그대로 가상의 DOM이라는 뜻입니다. React는 VDOM을 사용하는 게 특징인데요, VDOM은 React Element 노드로 이루어져 있습니다.</p>
<h2 id="🙋-vdom-트리는-무슨-역할을-하는-거예요">🙋‍ VDOM 트리는 무슨 역할을 하는 거예요?</h2>
<p>React는 State가 변경되면 리렌더링을 합니다. 그런데 전체를 리렌더링 하는 게 아니라, 변경된 State와 관련된 부분만 업데이트합니다. 다시 말하자면, 변경된 State와 관련이 없는 부분은 리랜더링 하지 않는 것입니다.</p>
<p>이게 가능한 이유는 VDOM 트리가 있기 때문입니다. VDOM 트리는 새로운 React Element로 인해 새롭게 만들어집니다. React는 새로운 VDOM 트리를 DOM 트리와 비교해서 DOM 트리가 업데이트 해야 할 부분을 찾습니다. 그리고 DOM 트리에 찾은 부분을 갱신합니다.</p>
<p>정리하자면, VDOM 트리가 존재하는 이유는 DOM 트리와 비교해서 DOM 트리를 업데이트 하기 위해서입니다. 우리는 이 과정을 재조정이라고 부릅니다. 만약 VDOM 트리가 없다면, 바뀐 요소를 찾지 않고 모든 요소를 DOM 트리에 갱신해서 전체를 리렌더링 하게 됩니다.</p>
<p><strong>재조정을 하기까지의 과정</strong></p>
<ol>
<li>JSX 작성한 코드는 React.createElement 코드로 변경된다.</li>
<li>React.createElement는 새로운 React Element를 만든다.</li>
<li>새로운 React Element는 새로운 React Element 트리(VDOM 트리)를 만든다.</li>
<li>VDOM 트리는 DOM 트리를 비교해서 다른 점을 찾는다. (재조정)</li>
<li>다른 점이 있다면 DOM 트리에 새로운 점을 갱신한다. (재조정)</li>
</ol>
<p><img src="https://velog.velcdn.com/images/kaori-killer/post/7344fd74-13d0-4b9b-8044-648f673b5b25/image.png" alt="재조정"></p>
<h2 id="🙋-vdom-트리는-왜-쓰는-거예요-react-element로-새로운-dom-트리를-만들면-새로운-vdom-트리-필요없이-dom-트리에-바로-업데이트가-가능하잖아요-vdom-트리를-쓰는-이유를-모르겠어요">🙋‍ VDOM 트리는 왜 쓰는 거예요? React Element로 새로운 DOM 트리를 만들면, 새로운 VDOM 트리 필요없이 DOM 트리에 바로 업데이트가 가능하잖아요? VDOM 트리를 쓰는 이유를 모르겠어요.</h2>
<h3 id="😀-vdom-트리는-충분한-빠르기를-제공한다">😀 VDOM 트리는 충분한 빠르기를 제공한다</h3>
<p>질문에 있듯이, VDOM 트리 없이 DOM 트리에 바로 적용하면 더 빠릅니다. 하지만 빠른 속도는 중요하지 않습니다. VDOM 트리를 이용해도 충분히 빠르기 때문입니다.</p>
<blockquote>
<p>React가 DOM보다 빠르다는 것은 잘못된 사실이고, 실제로는 유지 보수가 가능한 애플리케이션을 만드는 것을 React가 도와주고 대부분의 경우에서 Virtual DOM은 충분히 빠르다는 것입니다.</p>
<p>By Dan Abramov (Redux의 창시자)</p>
</blockquote>
<h3 id="😀-vdom-트리를-쓰는-이유는-유지보수-때문이다">😀 VDOM 트리를 쓰는 이유는 유지보수 때문이다</h3>
<p>React는 선언형 UI라는 더 나은 구조를 줍니다. 이게 VDOM 트리를 쓰는 이유입니다. 쉽게 말해보겠습니다. React를 사용하면, JSX로 작성만 해도 새로운 React Element를 생성하고 VDOM에 넣습니다. 그리고 실제 DOM과 재조정도 합니다. 개발자는 JSX 작성만 했는데, 나머지 부분이 알아서 이뤄지는 것입니다. 개발자는 선언만 했는데 UI가 자동으로 업데이트 되는 것입니다. 엄청 편하게 느껴지지 않나요?</p>
<p>만약에 선언형 UI가 아니라고 생각해봅시다. Element 하나를 수정해야 하는데, JQuery를 써서 바꿔야 하는 상황이면, DOM에서 해당 Element를 찾기도 힘들고 변경하는 것도 일이 될 것입니다.</p>
<h2 id="🙋-vdom-트리와-dom-트리를-같은-구조를-가져요">🙋‍ VDOM 트리와 DOM 트리를 같은 구조를 가져요?</h2>
<p>아니요. VDOM 트리와 DOM 트리는 같은 구조를 갖지 않습니다. VDOM 트리에는 있는 부분이 DOM 트리에는 없기도 합니다. 예를 들어, React.Fragment는 DOM 트리에는 없습니다.</p>
<h2 id="🙋-vdom-트리와-최적화-기법은-무슨-관계예요">🙋‍ VDOM 트리와 최적화 기법은 무슨 관계예요?</h2>
<p>VDOM 트리는 최적화 기법을 통해 동기화를 최적화 할 수 있습니다. 최적화 기법 중 하나인 key로 설명할겠습니다. 아래 예시를 봅시다. VDOM 트리의 값이 변경됐습니다. 이제 VDOM 트리와 DOM 트리를 동기화 해야 하는데 어떻게 해야할까요? 최적화를 하지 않으면, 모든 값이 바뀝니다. 하지만 만약 key를 이용한 최적화를 한다면, 숫자 1이 빠지고 숫자 7이 들어갔다고 동기화 할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/kaori-killer/post/e0e647b2-44ee-4e82-b252-2f3e77a00ff4/image.png" alt="동기화"></p>
<h2 id="🙋♂️-최적화-기법으로-react-훅만-있는-게-아니군요">🙋‍♂️ 최적화 기법으로 React 훅만 있는 게 아니군요?</h2>
<p>네, 최적화를 하기 위해서 React 훅인 useMemo, useCallback을 써봤을 것입니다. 하지만 이 방법만 있지도 않고, 최적화의 최우선의 방법도 아닙니다. 컴포넌트 분할만 잘 해도 최적화를 할 수 있습니다.</p>
<h2 id="😄-마무리">😄 마무리</h2>
<p>JSX와 VDOM에 대해 알아봤습니다. 생각보다 많은 사람들이 JSX와 VDOM에 대해 모르고 React를 쓰는 것 같습니다. 저도 잘 몰랐고요! 다음에는 다양한 최적화 기법에 대해 얘기해보겠습니다 😄</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모바일 환경에서 바텀시트 드래그 시 성능 저하가 발생하는 현상 디버깅하기]]></title>
            <link>https://velog.io/@kaori-killer/%EB%AA%A8%EB%B0%94%EC%9D%BC-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EB%B0%94%ED%85%80%EC%8B%9C%ED%8A%B8-%EB%93%9C%EB%9E%98%EA%B7%B8-%EC%8B%9C-%EC%84%B1%EB%8A%A5-%EC%A0%80%ED%95%98%EA%B0%80-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%ED%98%84%EC%83%81-%EB%94%94%EB%B2%84%EA%B9%85%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@kaori-killer/%EB%AA%A8%EB%B0%94%EC%9D%BC-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EB%B0%94%ED%85%80%EC%8B%9C%ED%8A%B8-%EB%93%9C%EB%9E%98%EA%B7%B8-%EC%8B%9C-%EC%84%B1%EB%8A%A5-%EC%A0%80%ED%95%98%EA%B0%80-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%ED%98%84%EC%83%81-%EB%94%94%EB%B2%84%EA%B9%85%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 15 Nov 2025 15:22:09 GMT</pubDate>
            <description><![CDATA[<h1 id="진단하기">진단하기</h1>
<p>바텀시트 애니메이션에 문제가 발생했다. 사용자가 바텀시트를 위아래로 드래그하면 이를 따라 부드럽게 이동해야 하지만, 애니메이션이 느리고 버벅였다. 바텀시트가 사용자의 손가락 움직임을 따라오지 못하고 프레임이 끊기는 현상이 나타났다.</p>
<p>문제는 다음과 같은 상황에서만 발생했다.</p>
<ul>
<li>모바일 기기에서만 발생 (데스크탑 환경에서는 문제 없음)</li>
<li>프로덕션 빌드에서만 발생 (개발 환경에서는 문제 없음)</li>
</ul>
<table>
<thead>
<tr>
<th>Before</th>
<th>After</th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://velog.velcdn.com/images/kaori-killer/post/ec3b30d5-8e19-4d40-97e5-bc98ce8f5046/image.gif" alt=""></td>
<td><img src="https://velog.velcdn.com/images/kaori-killer/post/7768cc04-190a-4da6-a5f7-c3693982d356/image.gif" alt=""></td>
</tr>
</tbody></table>
<h1 id="재현하기">재현하기</h1>
<h2 id="첫-번째-시도-데스크탑에서-성능-프로파일링">첫 번째 시도: 데스크탑에서 성능 프로파일링</h2>
<p>문제가 모바일에서만 발생한다는 점에서 낮은 CPU 성능이 원인일 것이라고 가정했다. 데스크탑 DevTools의 Performance 패널로 확인해보니, 애니메이션 구간에서 Main Thread 내 리플로우가 반복적으로 발생하고 있었다. 부분적 프레임 드랍도 발생했다.</p>
<p>애니메이션이 자연스럽게 보이려면 초당 60프레임(60FPS)을 안정적으로 그려야 한다. 드래그하는 동안 매 프레임마다 위치가 바뀌기 때문에, 그때마다 레이아웃을 다시 계산하면 CPU가 바빠질 수밖에 없다.</p>
<p>그래서 <code>transform: translateY</code>를 사용해 리플로우를 줄이는 방법을 시도했다. transform을 사용하면 브라우저 렌더링 과정에서 레이아웃과 페인트 단계를 생략하고 컴포지트 단계만 수행할 수 있다. 해당 요소가 별도의 레이어로 분리되고, GPU로 작업이 위임되기 때문이다.</p>
<blockquote>
<p><code>transform: translate3d(...)</code>, <code>will-change: transform</code> 속성도 함께 고려했다. 브라우저가 해당 요소를 미리 레이어로 분리하도록 유도해 애니메이션 진행 시 더 빠르게 반응할 수 있게 도와준다.</p>
</blockquote>
<h2 id="결과-효과는-미미했다">결과: 효과는 미미했다</h2>
<p>DevTools에서 CPU 부담이 다소 줄어든 것을 확인했고, 체감상으로도 조금 부드러워졌다. 그러나 모바일 기기의 프로덕션 환경에서는 여전히 느리고 둔한 움직임을 보였다.</p>
<h2 id="두-번째-시도-모바일-환경에서-직접-프로파일링">두 번째 시도: 모바일 환경에서 직접 프로파일링</h2>
<p>데스크탑 DevTools의 CPU Throttling 기능을 이용해 4×, 6× 정도로 성능을 낮춰봤지만 문제를 재현할 수 없었다. 20× slowdown에서는 페이지 렌더링 자체가 너무 느려 정상적인 테스트가 불가능했다.</p>
<p>그래서 실제 모바일 기기에서 프로덕션 빌드를 실행하고, 이를 데스크탑에서 DevTools로 확인하는 방식으로 문제를 재현하기로 했다.</p>
<ol>
<li>모바일 기기와 데스크탑을 케이블로 연결한다.</li>
<li>데스크탑 로컬에서 프로덕션 빌드를 띄운다.</li>
<li>모바일 기기에서 해당 로컬 서버에 접속한다.</li>
<li>데스크탑에서 모바일 기기의 DevTools를 열어 성능을 분석한다.</li>
</ol>
<blockquote>
<p><strong>데스크탑 로컬에서 프로덕션 빌드를 띄우는 방법</strong></p>
</blockquote>
<pre><code class="language-bash">npm run build
npx serve -s dist</code></pre>
<blockquote>
<p><strong>데스크탑에서 모바일 기기의 DevTools를 여는 방법</strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/kaori-killer/post/4e09c1c5-491b-4294-b746-291a3a2c2eb2/image.png" alt=""></p>
<p>모바일 환경에서 프로파일링 결과를 확인하니 문제가 명확하게 보였다.</p>
<ol>
<li>불규칙적으로 화면을 그리고 있다.</li>
<li>CPU의 최대 사용률이 93%까지 올라간다.</li>
<li>스타일 재계산이 매 터치 이벤트마다 발생한다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/kaori-killer/post/2efa225b-7a55-47bc-bdbe-8d1998411635/image.png" alt="BEFORE"></p>
<h2 id="찾아낸-원인">찾아낸 원인</h2>
<p>transform으로 해결되지 않는 이유를 알 수 있었다. transform은 브라우저 렌더링 단계 중 레이아웃과 페인트를 건너뛴다. 그러나 문제는 그 이전 단계인 <strong>스타일 재계산</strong>에서 리소스를 과하게 쓰고 있었다. 뒤에서 단계를 생략해도 앞에서 병목이 생기면 큰 효용을 얻지 못하는 것이다.</p>
<p>로직을 확인해보니 바텀시트 위치(positionPercent)를 React State로 관리하고 있었다.</p>
<pre><code class="language-typescript">const onPointerMove = (e) =&gt; {
  // 매 터치 이벤트마다 호출 (초당 60~120회)
  setPositionPercent(newValue); // React 상태 업데이트
};</code></pre>
<p>이 코드의 실행 흐름은 다음과 같다.</p>
<pre><code>터치 이벤트 발생 (초당 60~120회)
    ↓
setPositionPercent() 호출
    ↓
React 리렌더링
    ↓
Emotion 스타일 객체 재생성 및 해싱
    ↓
CSSOM 업데이트 + Style 재계산
    ↓
transform → GPU</code></pre><p>구현 당시에는 <code>위치 변경 → 리렌더링 → 화면 갱신</code>이 문제가 될 줄 몰랐다. React의 리렌더링이나 런타임 CSS-in-JS의 작업이 많은 것 역시 브라우저 메인 스레드의 부담을 주는 것이었다.</p>
<h2 id="수정하기">수정하기</h2>
<p>바텀시트 위치를 State로 상태관리했던 것을, useRef를 사용한 DOM 직접 조작으로 바꿨다. 선언적 패러다임을 일부 포기하고 성능이 중요한 구간에 명령형을 넣었다고 볼 수 있다.</p>
<pre><code class="language-typescript">const onPointerMove = (e) =&gt; {
  // React 상태 업데이트 없이 ref에만 저장
  currentPercentRef.current = clampedPercent;

  // DOM 직접 조작
  containerRef.current.style.transform = `translateY(${100 - clampedPercent}%)`;
};

const onPointerUp = (e) =&gt; {
  // 인라인 스타일 제거하여 React가 다시 제어
  containerRef.current.style.transform = &#39;&#39;;

  // 스냅할 때만 상태 업데이트
  setPositionPercent(target);
};</code></pre>
<p>갑자기 DOM 조작이라니, 괜찮을까? <a href="https://ko.react.dev/learn/manipulating-the-dom-with-refs">공식문서</a>를 찾아봤는데 괜찮다고 한다. useRef가 존재하는 이유이기도 했다.</p>
<blockquote>
<p>React는 렌더링 결과물에 맞춰 DOM 변경을 자동으로 처리하기 때문에 컴포넌트에서 자주 DOM을 조작해야 할 필요는 없습니다. 하지만 가끔 특정 노드에 포커스를 옮기거나, 스크롤 위치를 옮기거나, 위치와 크기를 측정하기 위해서 React가 관리하는 DOM 요소에 접근해야 할 때가 있습니다. React는 이런 작업을 수행하는 내장 방법을 제공하지 않기 때문에 DOM 노드에 접근하기 위한 ref가 필요할 것입니다.</p>
</blockquote>
<h1 id="결과">결과</h1>
<p>수정 후 Safari 프로파일러 결과는 극적으로 개선되었다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>Before</th>
<th>After</th>
</tr>
</thead>
<tbody><tr>
<td><strong>애니메이션</strong></td>
<td><a href="https://github.com/user-attachments/assets/8b839518-52ea-4fb4-af71-d548d6fff47f">실행영상</a></td>
<td><a href="https://github.com/user-attachments/assets/d6e2bf57-39f9-4bd0-931e-c3755a78ab94">실행영상</a></td>
</tr>
<tr>
<td><strong>퍼포먼스 탭</strong></td>
<td><img width="958" height="261" alt="스크린샷 2025-11-16 오전 3 02 27" src="https://github.com/user-attachments/assets/141ed4b0-38e4-4eb2-b933-639bef415153" /></td>
<td><img width="958" height="266" alt="스크린샷 2025-11-16 오전 3 02 09" src="https://github.com/user-attachments/assets/da169a01-0e75-42d8-a153-fa48cbf542ff" /></td>
</tr>
<tr>
<td><strong>CPU 사용률</strong></td>
<td>최대 92%, 불규칙적 프레임 드랍</td>
<td>최대 64%, 안정적인 60FPS 유지</td>
</tr>
<tr>
<td><strong>렌더링 파이프라인</strong></td>
<td>스타일 재계산 → CSSOM 업데이트 → 레이아웃 재계산 반복</td>
<td>Composite 단계만 실행</td>
</tr>
<tr>
<td><strong>상태 관리</strong></td>
<td>React State로 height 관리 (리렌더링 발생)</td>
<td>useRef로 DOM 직접 조작 (리렌더링 없음)</td>
</tr>
<tr>
<td>이제 개발/프로덕션 환경 구분 없이 바텀시트 애니메이션이 부드럽게 동작한다.</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h1 id="재발방지하기">재발방지하기</h1>
<p><strong>고빈도 업데이트에서 React 상태 사용 주의</strong></p>
<p>애니메이션이나 드래그처럼 초당 60회 이상 업데이트가 필요한 인터랙션에서는 React 상태 업데이트가 병목이 될 수 있다. 특히 CSS-in-JS 라이브러리를 함께 사용할 경우 스타일 재계산 비용이 추가된다. 이런 경우 <code>useRef</code>를 활용한 DOM 직접 조작을 고려해야 한다.</p>
<p><strong>실제 환경에서의 프로파일링 중요성</strong></p>
<p>개발 환경과 프로덕션 환경, 데스크톱과 모바일에서 동일한 코드가 다른 성능을 보일 수 있다. 실제 사용 환경에서 프로파일링을 수행하고, CPU와 네트워크 조건을 변경해가며 테스트하는 것이 중요하다. 앞으로 애니메이션 개발 시에는 다양한 환경에서 성능을 검증할 것이다.</p>
<h1 id="여전히-궁금한-점">여전히 궁금한 점</h1>
<ul>
<li>브라우저가 레이어를 나누는 규칙은 무엇일까?</li>
<li>React Profiler로 리렌더링이 얼마나 줄었는지 수치로 확인해볼 수 있을 것 같다.</li>
<li>프로덕션 빌드에서만 문제가 심했던 정확한 이유는 무엇일까? (React 배치 업데이트 차이? Emotion 최적화 방식?)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기 FE Lv.4] 3주차 회고]]></title>
            <link>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.4-3%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.4-3%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 22 Sep 2025 12:46:21 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/kaori-killer/post/70fc7122-4398-4da9-beed-475615639207/image.jpg" alt=""></p>
<h3 id="📌-pr-템플릿-바꾸자고-제안하기">📌 PR 템플릿 바꾸자고 제안하기</h3>
<p>이번 주에는 PR 템플릿을 바꾸자고 제안했다.<br>기존 문제와 개선 이유를 정리해서 팀에 공유했더니, 다행히 팀원들이 긍정적으로 수용해주었다.  </p>
<ul>
<li><strong>기존 문제 / 개선 이유</strong>  <ul>
<li>작업 내용에서 <strong>왜 이 작업을 하는지</strong> 맥락이 드러나지 않아 혼동이 있었다.  </li>
<li>스크린샷이나 영상은 케이스마다 다르게 첨부되어 일관성이 부족했다. 그래서 아예 고정된 위치에 첨부란을 두어 거의 필수적으로 넣도록 했다.  </li>
<li>리뷰 포인트도 작성자 마음에 달려 있어 깊이가 부족할 때가 있었는데, 이번에는 구체적으로 작성할 수 있는 구조를 넣었다.  </li>
<li>이슈를 기록할 공간이 없어, 발견한 문제를 공유하기 힘들었다. 템플릿에 이슈란을 추가했다.  </li>
<li>마지막으로 <strong>디스커션 공간</strong>을 만들어, 작업 중 고민이나 탐구한 내용을 자유롭게 정리할 수 있도록 했다.  </li>
</ul>
</li>
</ul>
<h4 id="after">AFTER</h4>
<pre><code class="language-jsx"># #️⃣ Issue Number

## 🕹️ 작업 내용
한 줄 요약:

- [ ] 
- [ ] 

## 📋 리뷰 포인트
-
-

## 🔮 기타 사항
-
-

#### AFTER

#️⃣ Issue Number
&lt;!-- 연결된 이슈 번호 (없으면 생략) --&gt;

## 🕹️ 작업 내용
&lt;!-- 왜(배경) → 무엇을(구체적 변경) --&gt;
- 배경/문제:
- 목표/의도: 

- [x] 주요 변경 1
- [x] 주요 변경 2
- [x] 주요 변경 3

## 🎨 Before / After (Optional)
&lt;!-- UI/UX 변경이 있다면 스크린샷, GIF 첨부 --&gt;
| Before | After |
|--------|--------|
| [before]() | [after]() | 

## 🔍 리뷰 포인트
&lt;!-- 리뷰어가 집중해야 할 부분 --&gt;
- [ ] 시나리오대로 정상 동작하는지 확인해주세요.
- [ ] 테스트 코드가 정상 동작하는지 확인해주세요.
- [ ] 로직/설계 검토 포인트

## ⚠️ 이슈 (Known Issues / TODO)
&lt;!-- 아직 해결되지 않은 문제나 후속 작업 예정 사항 --&gt;

## 💬 Discussion (깊은 의견 교환용)
&lt;!-- 단순 확인이 아닌, 리뷰어와 길게 나누고 싶은 설계/구조 고민 --&gt;

## 📎 참고 (Optional)
- 관련 문서/디자인/티켓 링크</code></pre>
<h3 id="📖-이번-주-영어">📖 이번 주 영어</h3>
<ul>
<li>it melts in your mouth  </li>
<li>I&#39;ll do my best, pick me  </li>
<li>beauty is in the eyes of the beholder  </li>
<li>the days are getting longer  </li>
<li>I love @@@, please give me extra if you can  </li>
<li>Come on in, the water&#39;s fine  </li>
<li>Ah~Really? (깨달음)  </li>
<li>Oho, that&#39;s too bad (예상 못 한 정보)  </li>
<li>Aw, I&#39;m sorry (기분 다운)  </li>
<li>thanks, but you don&#39;t have to do that  </li>
<li>jinx: 찌찌뽕  </li>
<li>It&#39;s dangerous, you have to go another way</li>
<li>Could you take this away, please  </li>
</ul>
<h3 id="✍️-글로-쓰고-싶은-주제">✍️ 글로 쓰고 싶은 주제</h3>
<ul>
<li>추상화 레벨 맞추기 vs 과한 추상화  </li>
<li><a href="https://ko.react.dev/blog/2024/12/05/react-19#ref-as-a-prop">React 19의 <code>ref</code> as prop 도입</a>  <ul>
<li>어떤 변화 덕분에 가능해졌을까?  </li>
</ul>
</li>
<li>이번 주엔 <a href="https://velog.io/@kaori-killer/%EC%9B%B9-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%8F%99%EC%9E%91%EC%9B%90%EB%A6%AC-Stack-Queue-Event-loop">웹 브라우저 동작원리 - Stack, Queue, Event Loop</a>를 공부했다.  </li>
</ul>
<h3 id="💡-깨달은-것">💡 깨달은 것</h3>
<ul>
<li>비동기 로직 이후에 보니 훨씬 재밌었고, 브라우저에서 비동기 처리가 어떻게 동작하는지 이해할 수 있었다.  </li>
<li>캉골이 프로젝트에서 <code>react-query</code>를 도입한 이유가 무효화/캐싱이라는 걸 알게 되었다. 내 무한 스크롤에도 적용하면 좋겠다는 생각이 들었다.  </li>
<li>현재 프로젝트는 지하철 경로만 API 요청을 하고 있는데, 나중에 버스까지 추가되면 더 좋을 것 같다. 그러면 지하철/버스를 오가며 여러 번 요청이 생겨서 캐싱 전략을 세워볼 기회도 있을 듯 🤩  </li>
<li>이번 주 수업에서는 &#39;단순히 AI를 쓰는 게 아니라, 잘 쓰는 것&#39;에 대해 배웠다.  <ul>
<li>특히 공식 문서를 활용하는 전략을 강조했는데, <a href="https://youtu.be/HqM6rlwAS-Q?feature=shared">프롬프트 엔지니어링 영상</a>과 <a href="https://docs.anthropic.com/ko/docs/welcome">Anthropic 문서</a>를 다시 정리해 두면 좋겠다.  </li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기 FE Lv.4] 2주차 회고]]></title>
            <link>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.4-2%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.4-2%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Thu, 18 Sep 2025 11:45:45 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/kaori-killer/post/9b94079c-43c9-467f-b8e2-a393dc986d7a/image.jpg" alt=""></p>
<h3 id="🔫-2달-남았다">🔫 2달 남았다</h3>
<p>앞으로 남은 시간은 불과 두 달. 시간이 정말 믿기지 않을 만큼 빠르게 흐른다. 요즘 아침 일찍 출근해 자바스크립트, 알고리즘, 리팩토링 같은 개인 공부를 이어가고 있다. 누군가 &quot;아침에 일찍 가는 게 힘들지 않냐&quot;고 물었다. 신기하게도 한 번도 힘들다고 느낀 적이 없다. 오히려 학교 다닐 때는 1교시 수업이 괴로웠는데, 지금은 일찍가서 1시간이라도 더 공부하고 대화할 수 있다는 사실 자체가 행복하고 설렌다.</p>
<p>우테코는 내가 늘 꿈꾸던 이상적인 교육 현장이다. 필요한 것을 필요할 때 배우고, 끊임없이 토론하며 성장하고 고민을 나눈다. 그리고 무엇보다 코치들의 진심 어린 노력에서 큰 감동과 원동력을 얻는다. 공원이 자신의 꿈을 이야기했을 때, 나 역시 그런 사람이 되고 싶다고 다짐했다. 남과 비교하지 않고 스스로를 위해 사는 삶, 공원의 말을 들으며 나도 그것이 나의 길이 되기를 바랬다.</p>
<h3 id="🆎-이번-주-학습한-영어-문장">🆎 이번 주 학습한 영어 문장</h3>
<p>이번 주에도 영어 공부를 빠뜨리지 않았다. 하루 10분 이상은 꼭 채웠다. 관심 있는 것을 매일 하다 보니 꾸준함이 자연스러워졌다. 작은 성취가 또 다른 습관으로 이어지고, 그 습관이 하루의 시작과 끝을 안정감 있게 만든다.</p>
<p>돌아보면 나의 가장 큰 장점은 꾸준함과 인내심인 것 같다. 누구에게나 있는 덕목일지 몰라도, 나는 쉽게 지루해하지 않고 오래 이어갈 수 있다. &quot;포기해야 하나?&quot;라는 생각보다 &quot;어려워서, 고통스러워서 더 재미있다&quot;는 감정이 더 크다. 그래서 지금도 앞으로도 계속 습관을 이어갈 수 있을 것같다.</p>
<ul>
<li>Could we have separate bills please?</li>
<li>Do you have any idea what time it is?</li>
<li>They&#39;re all good. I guess you have to watch em all.</li>
<li>It&#39;s really close, wanna walk?</li>
<li>I don’t know how I feel about that.</li>
<li>Are there any foods you don’t eat?</li>
<li>Just be straight with me, girl/man~</li>
<li>Wait, let me get the straight. You&#39;re saying deadline is tomorrow, right?</li>
<li>I&#39;m sorry, I didn’t mean it that way.</li>
<li>(Let it) in one ear and out the other.</li>
</ul>
<h3 id="🪞-배울-점--반면-교사">🪞 배울 점 / 반면 교사</h3>
<p>세 살 아이에게도 배울 점이 있다고 하는데,
이곳은 20, 30대 청년들이 모여 있으니 서로에게서 얻을 수 있는 배움은 말할 것도 없이 많다. 상대의 좋은 습관을 찾다보면 그 사람을 존경하게 된다. 그리고 닮고 싶어진다. 서로가 서로의 거울이 되어주는 것 같다.</p>
<p>[배울 점]</p>
<ul>
<li>피터<ul>
<li>회고하는 습관</li>
<li>정리하는 습관</li>
<li>정리한 걸 나누는 습관</li>
</ul>
</li>
<li>다이엔<ul>
<li>상대를 편하게 해주는 태도</li>
<li>경청 능력</li>
</ul>
</li>
<li>머핀<ul>
<li>높은 이해력</li>
<li>말 정리를 잘하는 능력</li>
<li>말을 재미있게 하는 표현력</li>
</ul>
</li>
<li>캉골<ul>
<li>공감 능력</li>
<li>통제 능력(신앙심 기반)</li>
</ul>
</li>
</ul>
<p>[반면교사]</p>
<p>개인 메모.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기 FE Lv.4] 1주차 회고]]></title>
            <link>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.4-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.4-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 08 Sep 2025 08:32:54 GMT</pubDate>
            <description><![CDATA[<h3 id="이번주-개인-미션-성능-최적화">이번주 개인 미션: 성능 최적화</h3>
<p>이번주는 최적화에 대한 개인 미션을 진행했다.</p>
<p>성능을 개선할 때는 2가지 질문이 필요했다.</p>
<ul>
<li>첫째, &quot;왜?&quot; - 이유/시기</li>
<li>둘째, &quot;어떻게&quot; - 수치</li>
</ul>
<p>이 두 질문은 모두 사용자 경험을 위한 것이다.
예를 들어, 이미지를 최적화했는데 화질이 지나치게 떨어져 사용 경험이 나빠진다면, 그건 좋은 최적화라고 할 수 없다. 질문에 대해 &#39;잘&#39; 대답해서 사용자 경험이 좋도록 해야 한다.</p>
<h3 id="최적화-과정의-흐름">최적화 과정의 흐름</h3>
<p>미션을 하면서 느낀 성능 최적화의 전형적인 흐름은 다음과 같았다.</p>
<ol>
<li>성능 측정</li>
<li>문제 정의</li>
<li>원인 분석</li>
<li>가설 세우기</li>
<li>문제에 맞는 해결책 적용</li>
<li>성능 수치 개선 및 관리</li>
</ol>
<p>이 과정은 평소에 하던 디버깅 과정과 크게 다르지 않았다.
그리고 디버깅과 마찬가지로 해결책보다 문제 정의가 더 중요했다.
이유는 해결책은 너무 많고 또 기술 변화에 따라 계속 달라지지만, 문제가 발생하는 본질은 크게 바뀌지 않기 때문이다.</p>
<h3 id="신이-내린-축복-망각">신이 내린 축복, 망각</h3>
<p>요즘 마음이 한결 가볍다.
&quot;망각은 신이 내린 축복&quot;이라는 말을 실감하는 요즘이다.</p>
<p>그동안은 &quot;기억해야 할 것&quot;과 &quot;잊어야 할 것&quot;의 경계를 제대로 긋지 못했다.
모두 붙잡으려 하다 보니, 불필요한 반추가 습관이 되고,
좋지 않은 기억들까지 마음속에 쌓여 갔다.</p>
<p>그런데 사실 망각은 생각보다 단순했다.
그냥 잊으면 된다. 그리고 시간을 흘려보내면 된다.
그러면 정말로 잊혀졌다.</p>
<p>그래서 요즘 집중하고 있는 건,
무엇을 기억하고, 무엇을 흘려보낼지 선을 긋는 일이다. 그 기준을 세우고 있다.</p>
<h3 id="레벨-4-오히려-좋아">레벨 4 오히려 좋아...</h3>
<p>이전 레벨에서는 미션 때문에 늘 바빴다.
그래서 기본 개념을 차분히 공부할 시간이 없었다.
&quot;언제 한 번 제대로 기본기를 다질 수 있을까?&quot; 늘 마음에 걸렸다.</p>
<p>그런데 여러 삽질을 다 하며 코드를 짜 본 뒤 책을 읽으니, 오히려 더 재밌고 잘 이해된다.
경험을 떠올리며 대입해 보니 책 속 내용이 술술 들어왔다.
오늘 공부한 자바스크립트 비동기 파트도 팀 프로젝트에서 계속 했던 부분이라 자연스럽게 이해됐다.</p>
<h3 id="action-plan">Action Plan</h3>
<p>앞으로는 꾸준히 이 네 가지를 해보려 한다.</p>
<ul>
<li><input disabled="" type="checkbox"> 리팩터링 책 읽고 → 팀 프로젝트에 적용하기</li>
<li><input disabled="" type="checkbox"> 이력서 초안 작성하기</li>
<li><input disabled="" type="checkbox"> 알고리즘 문제 풀기 → 일단은 많이 풀어보는 걸 목표로</li>
<li><input disabled="" type="checkbox"> 자바스크립트 개념 공부 → 가장 궁금한 것부터 차근차근</li>
</ul>
<ul>
<li><input disabled="" type="checkbox"> 매주 일요일 회고 작성하기</li>
<li><input disabled="" type="checkbox"> 매주 저녁먹고 캉골과 1:1 영어 공부하기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기 FE Lv.4] 당황스러운 질문에는 이유가 있다]]></title>
            <link>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.4-%EB%8B%B9%ED%99%A9%EC%8A%A4%EB%9F%AC%EC%9A%B4-%EC%A7%88%EB%AC%B8%EC%97%90%EB%8A%94-%EC%9D%B4%EC%9C%A0%EA%B0%80-%EC%9E%88%EB%8B%A4</link>
            <guid>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.4-%EB%8B%B9%ED%99%A9%EC%8A%A4%EB%9F%AC%EC%9A%B4-%EC%A7%88%EB%AC%B8%EC%97%90%EB%8A%94-%EC%9D%B4%EC%9C%A0%EA%B0%80-%EC%9E%88%EB%8B%A4</guid>
            <pubDate>Sat, 06 Sep 2025 08:10:42 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/kaori-killer/post/f5679991-ed4c-4d15-bcfe-a53f7a37cae8/image.png" alt=""></p>
<p>&quot;꿈이 뭐야?&quot;, &quot;이상형이 뭐야&quot;, &quot;대학교는 어디야?&quot; 같은 질문들은 당황스럽다.
말할 게 없어서가 아니라, 무슨 의도로 물어본 건지 알 수 없어서 대답이 망설여진다. 그래서 보통 되묻는다.</p>
<p>&quot;어떤 꿈을 말하는 거야? 5년 후의 목표 같은 거?&quot;
&quot;이상형은 외모? 성격?&quot;
&quot;대학교는 왜 궁금한데?&quot;</p>
<p>흔한 질문이라 대충 맥락을 추측하고 답하곤 하지만, 가끔은 추측이 빗나간다. 답을 하고 나서야 상대가 궁금해한 건 다른 지점이었다는 걸 깨닫기도 한다.</p>
<p>이 경험은 코드리뷰와 닮아 있다.
PR 본문에 맥락이나 예시, 그리고 어떤 부분을 중점적으로 봐줬으면 하는지가 없다면 리뷰어는 추상적으로 느낄 수 밖에 없다. 어디를 집중해 살펴야 할지 알 수 없어 추측으로 대응하게 된다. 그러다 보면 &quot;어? 이 부분은 리뷰가 필요 없는 건데...&quot;라는 상황이 생긴다. 그래서 리뷰를 요청할 때는 의도를 분명히 드러내는 게 중요하다.</p>
<p>결국 매끄럽고 빠른 대화, 코드리뷰는 맥락과 구체성에서 비롯되는 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기 FE Lv.4] 단기적으로 창의성 늘리는 방법]]></title>
            <link>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.4-%EB%8B%A8%EA%B8%B0%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%B0%BD%EC%9D%98%EC%84%B1-%EB%8A%98%EB%A6%AC%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.4-%EB%8B%A8%EA%B8%B0%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%B0%BD%EC%9D%98%EC%84%B1-%EB%8A%98%EB%A6%AC%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Wed, 03 Sep 2025 01:16:41 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/kaori-killer/post/81b7f2da-4bb9-4ba3-8531-5e044b0b76c2/image.jpg" alt=""></p>
<p>오전에 준이 진행했던 &#39;단기적으로 창의성 늘리는 방법&#39;을 정리했다.</p>
<h4 id="1-양을-늘리기">1) 양을 늘리기</h4>
<p>알고리즘을 예로 들면, 1문제를 풀어도 다양한 접근 방식을 해보는 것.
10가지 이상으로 풀어보기, 완전히 다른 방식으로.</p>
<h4 id="2-사고방식-경험이-다른-사람과-대화하기">2) 사고방식, 경험이 다른 사람과 대화하기</h4>
<p>예를 들어, 당신이 양자역학을 초등학생에게 설명해야 한다면? 답변은 다양할 수 있다. &#39;초등학교 과학 선생님&#39;, &#39;양자역할을 잘 아는 초등학생&#39;, &#39;물리학자&#39;, &#39;SF 감독&#39;. 나와 다른 답변을 하는 사람과 대화해보기.</p>
<h4 id="3-문제-재정의">3) 문제 재정의</h4>
<p>스코프를 좁혔다/늘렸다.</p>
<ul>
<li>&#39;더 어렵게&#39; 혹은 &#39;더 쉽게&#39; 바꿔보기. 2주의 시간이 주어졌다면, 3시간으로 생각하고 진행하본다던가. 그럼 가장 중요한 핵심 1개를 찾게 됨.</li>
<li>생각을 뒤집는 것. 안 좋은 걸 좋게 생각해보기. (오히려 좋아 마인드)</li>
</ul>
<h4 id="4-강제-연상오감-활용-metaphor">4) 강제 연상/오감 활용 (Metaphor)</h4>
<p>한 가지에 집중하다보면 매몰될 수 있다. 생각의 전환이 필요한 시점이다.
이때 사물/자연물(ex. 슬리퍼/화분)을 이용해서 현재 상황과 연관지어 생각해보는 것.</p>
<p>ex) 현재 상황은 저 나무의 어디쯤일까? 뿌리쯤? 잎쯤? 뿌리라면 현재 기초를 다지는 것에 더 충실해보자</p>
<h4 id="5-조각내서-생각하기">5) 조각내서 생각하기</h4>
<p>&#39;노인 여가를 즐겁게&#39;라는 주제가 있다면, 이를 하나로 생각하지 않고
&#39;노인&#39; / &#39;여가&#39; / &#39;즐겁게&#39;로 쪼개서 생각해보는 것.
그리고 각 키워드 내에서 또 가지치기를 해보는 것.</p>
<h4 id="6-반대예외-생각하기">6) 반대/예외 생각하기</h4>
<ol>
<li>패턴인식: 일반적 패턴 분석(=추상화하기 ex. 디자인패턴)</li>
<li>예외발견: 반대로 생각하기</li>
<li>사고의 재구성</li>
</ol>
<p>ex) 계란 후라이 굽는 영상을 유튜브에 올리려고 하는데 &#39;썸네일 제목&#39;을 짓는다면? </p>
<ul>
<li>일반적인 패턴: 맛있는 계란 후라이 굽기</li>
<li>예외 발견: 불/기름/뚜껑</li>
<li>사고의 재구성: 들기름을 이용해서 계란 후라이를 구우면 더 맛있다?</li>
</ul>
<h4 id="action-plan">Action Plan</h4>
<ul>
<li>미션을 할 때, 스코프를 줄여보며 &#39;가장 중요한 것&#39;, &#39;당장 해야할 것&#39;에 집중해보기</li>
<li>AI 프롬프팅 할 때 <code>~3가지 방식 제안해줘</code> -&gt; <code>~3가지 방식을 서로 다른 관점에서 제안해줘</code>로 관점 넓혀서 결과 받아보기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기 FE Lv.3 방학] 優しい(상냥함)의 도시, 요나고]]></title>
            <link>https://velog.io/@kaori-killer/%E5%84%AA%E3%81%97%E3%81%84%EC%83%81%EB%83%A5%ED%95%A8%EC%9D%98-%EB%8F%84%EC%8B%9C-%EC%9A%94%EB%82%98%EA%B3%A0</link>
            <guid>https://velog.io/@kaori-killer/%E5%84%AA%E3%81%97%E3%81%84%EC%83%81%EB%83%A5%ED%95%A8%EC%9D%98-%EB%8F%84%EC%8B%9C-%EC%9A%94%EB%82%98%EA%B3%A0</guid>
            <pubDate>Mon, 01 Sep 2025 03:05:10 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/kaori-killer/post/7020f012-3673-46c8-9666-056f42ed0507/image.png" alt=""></p>
<p>레벨 3를 끝내고 방학을 보내고 있습니다. 우테코의 마지막 방학이기도 합니다.
잠깐 휴식을 위해 친구와 일본에 다녀왔습니다. 돗토리현에 있는 요나고라는 소도시였습니다.
도쿄/후쿠오카와는 다르게 한국인은 거의 없는 현지인이 대부분인 곳이었습니다.</p>
<p>요나고는 &#39;優しい(상냥한, 온화한)&#39;라는 말, 그 자체인 곳이었습니다.
서툰 일본어 실력에도 일본어로 끝까지 말할 수 있도록 도와주셨고,
말이 잘 통하지 않으면 번역기를 바로 꺼내주셨습니다. 이 과정에서 귀찮은 기색은 전혀 보이지 않았습니다. 오히려 늘 미소를 지어주셨습니다. </p>
<p>손님의 신분이었을 때 뿐만 아니라, 
길을 걷다 아이와 부딪혀도 아이는 제게 &#39;失礼しました(실례했습니다)&#39;라고 했습니다. 
작은 입에서 공손한 말이 나와 놀랐습니다. 아주 조그마한 예의가 바른 아이었습니다.</p>
<p>또, 마트에서 머리가 희끗한 노인이 더 연로해 보이는 노인의 문을 잡아주고 카트를 뽑아 건냈을 때도,
직원분들 중 몇몇이 나이가 많고 거동이 조금은 불편해보이는 노인일 때도(고용해준다는 것에 놀라서),
따뜻함을 느꼈습니다. </p>
<p>누구나 누구에게나 자연스럽게 배려를 하는 게 좋았습니다.
쉽게 &#39;ありがとう(감사합니다)&#39;와 &#39;すみません(죄송합니다)&#39;를 하는 분위기가 좋았습니다.
그래서 저도 평소보다 더 많이 쉽게 감사와 사과를 말했습니다.</p>
<p>관광객이기에 이것을 일본 문화라 단정할 수는 없습니다. 하지만 분명 배울 점이 많다고 느꼈습니다.</p>
<p>예전에 수업 시간에 만난 일본인은 정이 많던 시대가 있었다고 했습니다. 그리고 많은 사람들이 그 시대를 그리워한다고 했습니다. 이는 &#39;쇼와 노스탤지어(昭和ノスタルジー)&#39;로 단순히 경제성장/풍족함 때문에 돌아가고 싶은 게 아니라 유대가 있던 시대를 그리워하는 것입니다.</p>
<p>&#39;쇼와 노스탤지어(昭和ノスタルジー)&#39;, 일본의 당대는 어떤 분위기었을까 생각을 했습니다.
지금보다 더 &#39;優しい(상냥한, 온화한)&#39;한 시대일 수도 있고 아닐 수도 있고, 지금이 그 영향의 잔재일 수도 있고.
일본이 더 궁금해지는 여행이었습니다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기 FE Lv.3] 25, 26, 27 주차 회고]]></title>
            <link>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.3-25-26-27-%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.3-25-26-27-%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Wed, 27 Aug 2025 02:01:23 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/kaori-killer/post/f438fe29-ebaf-4e49-b75c-14d00c9a534b/image.jpg" alt=""></p>
<hr>
<h1 id="테스트">테스트</h1>
<p>[테스트 케이스를 공유한 이유]
테스트 케이스를 작성하고 PR 본문에 공유했다. 해당 PR에서 무엇을 했는지 테스트 케이스로 공유할 수 있어 좋았다. 테스트 케이스만 봐도 어느 영역까지 커버했는지 알려줄 수 있었다.</p>
<p><img src="https://velog.velcdn.com/images/kaori-killer/post/035bc7e9-f40f-4518-a9c3-fbdd01366980/image.png" alt=""></p>
<p>[스토리북에 디자인 토큰이 꼭 필요한가?]
팀원은 피그마에 있는 디자인 토큰을 스토리북에 넣고 싶어 했다. 하지만 나는 반문했다. 
&quot;그게 정말 스토리북에 필요한가요?&quot; 
장점도 있었다. 피그마를 열지 않고도 디자인 토큰을 확인할 수 있다는 점이다. 하지만 나는 유지보수 관점에서 반대했다.</p>
<ul>
<li>피그마와 스토리북 간 싱크가 깨져 SSOT가 무너질 수 있다.</li>
<li>싱크를 맞출 때마다 불필요한 커밋과 PR이 발생한다.</li>
<li>결국 스토리북이 유지보수 부담이 되는 도구로 전락할 수 있다.</li>
</ul>
<p>그래서 피그마를 직접 열어 확인하는 편이 더 낫다고 생각했다.</p>
<h1 id="cherry-pick--merge-into-branch">Cherry-pick / Merge into Branch</h1>
<p>[Cherry-pick]
A 브렌치에서 개발한 기능 중 3개의 커밋을 B 브랜치로 가져올 때 사용했다.</p>
<p>[Merge into Branch]
Dev로 머지해야 하는 상황에서, 먼저 Dev를 최신 상태로 업데이트한 뒤 한 번 병합하고 Dev로 보낼 때 사용했다.</p>
<h1 id="계속해서-잡아주는-것">계속해서 잡아주는 것</h1>
<p>팀 프로젝트가 완전히 끝난 뒤에 주는 피드백은 서로 성장에 도움이 되지 않는다고 생각한다. 오히려 다 끝나고 나서 받으면 &#39;왜 미리 말 안했어?&#39;라는 생각이 들며 기분이 상할 수도 있다. 
그래서 프로젝트를 진행 하면서 지속적으로 피드백을 주는 것이 중요하다고 본다. 또, 서로를 존중하고 신뢰하기 때문에 그 피드백을 공격으로 듣지 않는 것도 중요하다.</p>
<p>레벨 3 팀 프로젝트 동안 나는 팀원들에게 꾸준히 피드백을 했다. 여러 가지를 한꺼번에 지적하기보다, 가장 개선되었으면 하는 한 가지에 집중해 전달했다. 팀원이 그 피드백을 받아들이고 개선하려는 모습을 보이면, 나는 그것이 잘 자리 잡을 수 있도록 계속해서 반복해 강조했다.</p>
<p>피드백을 줄 때 처음에는 많이 망설였다. 말이 칼날이라도 되는 듯, 혹여 상대를 베는 건 아닐가 조심스러웠다. 하지만 의외로 여러 번 해보니, 가볍게 말하는 것이 가장 자연스럽고 효과적이었다. 상대는 내가 이런 피드백을 줄 거라고 전혀 예상하지 못했다고 했다. 결국 나만 고민하고 있던 부분이었던 것이다. 이런 피드백일수록 빨리 공유하고, 함께 생각을 맞추는 게 낫다고 느꼈다.</p>
<blockquote>
<p>피드백 예시)</p>
<p>리뷰에서 바로 구체적인 구현 예시를 제시하기보다는, 먼저 방향성을 함께 논의한 후 필요한 경우 예시를 공유하는 방식이 서로의 성장에 더 도움이 될 것 같다는 생각이 듭니다. 이유는 ~ 중략</p>
</blockquote>
<h1 id="마무리">마무리</h1>
<p>앞으로는 글을 좀 더 가볍게 써보려 한다. 완성도를 높이려고 하다보니 포스팅이 밀리게 됐다. 완성도보다 더 자주 쓰는 것에 신경쓰려 한다. (회고하는 형식 자체를 바꿀 수도 있다)</p>
<p>마무리는 트위터에서 발견한, 내가 좋아하는 사람의 글을 대신한다.</p>
<blockquote>
<p>사람과 사람 사이에 관계는 원래 폐를 끼치는 거라 생각해요. 물론 그러지 않으려 노력하고 걱정하고 배려하는 태도는 멋지고, 그런 따뜻한 사람들을 좋아하지요. 하지만 선생이던 친구던 사랑하는 사람이던 자식이던 동료 시민이던. 서로 &quot;피해를 주지 않을 것&quot;을 요구하기 시작하면 황폐해져요.</p>
<p><strong>-</strong> 탐정 토끼 <strong>-</strong></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기 FE Lv.3] 23, 24주차 회고]]></title>
            <link>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.3-23-24%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.3-23-24%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 04 Aug 2025 11:04:09 GMT</pubDate>
            <description><![CDATA[<p>요즘은 이렇게 닌자 도시락을 싸서 다닌답니다 🥷🍱</p>
<hr>
<h1 id="🐾-서로-발을-맞춰가는-과정">🐾 서로 발을 맞춰가는 과정</h1>
<p>초기 기획이 마무리되고 본격적으로 프론트엔드 개발을 시작했다. 우리 팀은 FE 인원이 2명이다. </p>
<p>처음 개발하는 사이다 보니, 서로 맞지 않는 부분이 있었다. 이때, 이 부분을 단순한 불만으로 표출하지 않고 싶었다. 객관적으로 풀어보고자 했다. [문제상황 - 원인 - 가설]을 정리한 뒤 팀원에게 솔직하게 공유했다. 그리고 잘 합의를 봤다.</p>
<p>아주 초반에 있었던 일화를 하나 소개한다.</p>
<h2 id="1️⃣-문제-상황--원인">1️⃣ 문제 상황 &amp; 원인</h2>
<p>초반엔 &quot;둘 다 경험해보자&quot;는 방향으로 같은 테스크를 두 명이 함께 해봤다. 처음엔 괜찮았지만, 시간이 부족해지면서 병렬 작업이 오히려 비효율을 만들었다.</p>
<ul>
<li><p>문제: 같은 테스크를 동시에 작업하거나, 먼저 하는 사람이 처리하게 됐다.
→ 누가 어떤 테스크를 맡을지 정하지 않았기 때문.</p>
</li>
<li><p>테스크의 중요도와 상관없이 아무거나 먼저 잡아서 하게 됐다.
→ 우선순위 기준이 없어서 무작위로 진행됐던 것 같다.</p>
</li>
<li><p>조급한 마음에 쉬는 시간이나 퇴근 후에도 계속 작업하게 됐다.
→ 마감 기한이 명확하지 않다 보니 &quot;빨리 해야 더 많이 할 수 있다&quot;는 압박이 생겼다.</p>
</li>
</ul>
<h2 id="2️⃣-가설">2️⃣ 가설</h2>
<ul>
<li><p>가설 1: 테스크별 담당자를 미리 정한다.
→ 작업이 겹치지 않는다.</p>
</li>
<li><p>가설 2: 테스크를 사전에 정리하고, 우선순위를 명확히 설정한다.
→ 중요한 작업부터 집중할 수 있어 불필요한 시간 낭비를 줄일 수 있다.</p>
</li>
</ul>
<h2 id="☕️-후기">☕️ 후기</h2>
<p>솔직하게 말하는 건 생각보다 어려운 일이었다. 평소에 말을 많이 나눠본 팀원이 아니라서, 그 사람이 어떤 성격을 가졌는지, 어느 정도까지 이야기해도 괜찮을지 감을 잡기 어려웠다. 하지만 나 스스로 불편함을 느끼고 있었기에, 결국엔 솔직하게 공유해보기로 마음먹었다. 대화를 하기 전에 메모에 말할 내용을 정리해두었다. 최대한 명확하게 설명하고 싶었다. 팀원을 탓하는 게 아니라, &quot;나는 ~이런 부분에서 어려움을 느꼈고, 우리가 ~이렇게 해보면 더 좋을 것 같아.&quot; 라는 메시지를 전하고 싶었다. 다행히 팀원은 굉장히 긍정적으로 반응해주었다. &quot;사실 나도 은연중에 이런 생각을 하고 있었어, 먼저 말해줘서 고마워&quot;라고 해줬다. 용기내길 잘했다고 느꼈다. </p>
<p>이 대화를 계기로, 팀원이 개발할 때 어떤 성향을 가졌는지, 이 팀 프로젝트에서 무엇을 중요하게 생각하고, 어떤 방향으로 성장하고 싶은지도 얘기했다. 그 덕분에 단순한 작업 분배를 넘어서, 서로의 성향과 가치관을 이해하고, 더 나은 협업 방향을 함께 고민할 수 있게 됐다 😀</p>
<p>현재는 서로 솔직하게 피드백을 주기 위해 주 2회 피드백 시간을 갖고 있다.</p>
<h1 id="🤖-테스트를-쓰자">🤖 테스트를 쓰자!</h1>
<p>이전 프로젝트에서 가장 불편했던 점은, 기능을 붙일 때마다 이전 기능이 잘 작동하는지 매번 수동으로 확인해야 했던 거였다. 확신 없이 기능을 붙이게 되고, 동료가 만든 기능도 마찬가지였다. 어떤 범위까지 검증하고 있는지 명확히 알 수 있는 명세가 필요했다.</p>
<p>그래서 테스트의 중요성을 절실히 느꼈다. </p>
<p>이번 프로젝트에서는 단위 테스트도 좋지만, 관련 있는 기능들을 묶은 통합 테스트도 하고 싶고,
사용자 흐름은 E2E 테스트로 커버하고 싶다.</p>
<p>장기적으로 언젠가 도움이 되는 걸 위해서 테스트를 짜는 게 아니라,
일단 지금 당장 도움이 되는 테스트를 짜봐야지!</p>
<blockquote>
<p>시지프</p>
<p>테스트의 필요성을 느꼈다면, 직접 시도해보는 것이 중요하다.
어렵게 생각하지 말고 가볍게라도 붙여보자.
AI의 도움을 받아도 좋고, 테스트 코드는 description만 잘 작성해도 충분하다.
실제로 토스에서도 많은 개발자들이 테스트를 작성하며, 이는 개발 역량 향상에 도움이 된다.
그러니 부담 갖지 말고 일단 해보자!</p>
</blockquote>
<h1 id="👑-코드에-금칠하지-말자">👑 코드에 금칠하지 말자</h1>
<p>브라운이 밥을 먹으며 해준 말이 있다. &quot;코드에 금칠하지 말자.&quot;</p>
<p>문제가 생기기도 전에 과하게 미리 해결하려 들지 말라는 의미로 다가왔다.</p>
<p>예를 들면,
요즘 핫하다는 기술을 무작정 쓰는 것,
멋진 인프라 아키텍처를 괜히 갖추는 것,
성능 테스트를 지나치게 반복하는 것.</p>
<p>이런 데 휘둘리는 게 아니라,
지금 우리가 가진 자원으로 어떤 문제까지 해결할 수 있느냐를 봐야할 것 같다.</p>
<p>작은 힘으로 얼마나 큰 문제를 해결할 수 있는지, 그리고 거기서 얼마나 큰 효과를 낼 수 있는지를 지켜보자.</p>
<h1 id="🍵-마무리">🍵 마무리</h1>
<p>요즘 좋아하는 문장으로 회고를 마무리 한다 🌟</p>
<blockquote>
<p>어려운 게 아니라, 익숙하지 않아서 그래!</p>
</blockquote>
<blockquote>
<p>일어나지 않은 일에 대해 걱정하지 않기!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기 FE Lv.3] 22주차 회고]]></title>
            <link>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.3-22%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.3-22%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sat, 19 Jul 2025 15:07:25 GMT</pubDate>
            <description><![CDATA[<p>친해지기 위해 팀원들과 보드게임 카페를 갔다. 한 게임을 2시간 정도 했다. &quot;왜 게임이 안 끝나지?&quot;라고 하면 게임이 더 길어지는 웃긴 게임이었다 🤣</p>
<hr>
<h1 id="아직-아이디어가-확정되지-않았다">아직 아이디어가 확정되지 않았다</h1>
<p>아직 아이디어가 확정되지 않았다. 아이디어를 구체화하고 확장해 나가는 과정에서, 팀원들이 생각하는 서비스의 핵심 페인포인트가 조금씩 달랐다. 다시 하나로 통일해 가는 과정이 필요해 보인다.</p>
<p>원래 이번 주 금요일이 마감이었지만, 주말까지 충분히 고민해보고 월요일에 최종 확정 짓기로 했다.</p>
<h1 id="아직-적응하는-중이다">아직 적응하는 중이다</h1>
<p>낯선 사람들과 팀을 이루는 건 이번이 처음이었다. 심리적 안정감이 없는 상태에서 어떻게 하면 서로의 기분을 상하게 하지 않을까 계속 고민했다. 예를 들어, 상대의 의견에 대해 말할 때는 긍정적인 의견을 먼저 말하고 그 다음에 개선점을 말한다던가. 나도 평소 같았으면 안정감이 없이는 말을 잘 하지 않았을 텐데, 다행히 팀원들이 내 이야기를 잘 들어주고 고려해줘서 약간은 마음 놓고 의견을 낼 수 있었다.</p>
<p>페어 프로그래밍을 하던 중, 메타가 한숨을 자주 쉬는 모습을 보고 조금 속상했다. 혹시 3명이서 진행하는 게 너무 힘들어서 그런건 아닐까 걱정도 됐다. 그런데 알고 보니 메타는 팀 프로젝트 때문에 힘들었던 게 아니라, 회사에 합격하게 돼서 우테코를 나갈지 말지를 고민 중이었던 것이다. 그 이야기를 나중에 듣고 안심할 수 있었다. (메타 좋은 곳으로 가게 된 거 정말 축하해~!!! 🥳)</p>
<p>이번 주는 회의를 정말 많이 했다. 하루에 3~4시간은 기본이었고, 쉴 틈 없이 이어지는 긴 회의는 에너지를 많이 소모하게 했다. 그래서 누군가 농담을 던져도 내가 잘 웃어주지 못한 게 미안했다. 다음 주에는 회의 시간을 줄이고, 틈틈이 쉬는 시간을 갖자고 이야기가 나왔다. 에너지를 잘 관리하며 더 즐겁게 참여해야겠다.</p>
<blockquote>
<p>노르웨이의 숲 - 무라카미 하루키</p>
<p>모든 걸 너무 심각하게 생각하지 않는 것, 모든 것과 나 사이에 적절한 거리를 두는 것.</p>
</blockquote>
<blockquote>
<p>노르웨이의 숲 - 무라카미 하루키</p>
<p>가장 중요한 건 서두르지 않는 거야. 이건 내가 하는 또 하나의 충고야. 서두르지 말 것. 도저히 감당할 수 없을만큼 꼬이고 또 꼬여도 절망적인 기분에 빠지거나 다급한 마음에 억지도 끌어내려 해서는 안 돼. 충분히 시간을 들이다는 생각을 갖고 하나하나 천천히 풀어 나가야만 해. 할 수 있겠어? 기다림은 고통스러워. 특히 자기 나이 때는.</p>
</blockquote>
<h1 id="쉬는-취미가-생겼다">쉬는 취미가 생겼다</h1>
<p>쉬는 취미가 생겼다. 조금 할머니 같은데... 사우나이다. 사우나는 도전하는 취미와 달리 아무 노력도 하지 않아도 된다. 그저 쉬면 된다. 죄책감 없이 쉬어도 된다는 게 좋다. 나를 위해 쉰다는 것은 멋찐 일이다. 쉬는 취미를 갖게 됐음에 감사하다 !</p>
<h1 id="🍵-마무리">🍵 마무리</h1>
<p>팀 프로젝트 1주차를 잘 마쳤다. 앞으로가 기대된다. 화이팅!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모잇지] Git, 팀 컨벤션 맞추기]]></title>
            <link>https://velog.io/@kaori-killer/%EB%AA%A8%EC%9E%87%EC%A7%80-Git-%ED%8C%80-%EC%BB%A8%EB%B2%A4%EC%85%98-%EB%A7%9E%EC%B6%94%EA%B8%B0</link>
            <guid>https://velog.io/@kaori-killer/%EB%AA%A8%EC%9E%87%EC%A7%80-Git-%ED%8C%80-%EC%BB%A8%EB%B2%A4%EC%85%98-%EB%A7%9E%EC%B6%94%EA%B8%B0</guid>
            <pubDate>Sat, 19 Jul 2025 14:04:53 GMT</pubDate>
            <description><![CDATA[<h1 id="✍️-이-글을-작성한-이유">✍️ 이 글을 작성한 이유</h1>
<p>이전에는 했던 팀 프로젝트에서는 Git 컨벤션을 세세히 정하지 않았다. 그래서 그때는 브랜치명을 각자 마음대로 정했고, 여러 기능을 동시에 개발할 때는 어떤 브랜치가 어떤 이슈를 다루고 있는지 헷갈릴 때가 많았다.</p>
<p>이번에는 팀원들과 함께 Git 컨벤션을 처음부터 꼼꼼히 정해보았고, 잘 정리했기에 이렇게 글로 남긴다.</p>
<h1 id="👍-컨벤션이-좋은-이유">👍 컨벤션이 좋은 이유</h1>
<p>컨벤션 덕분에 브랜치를 만들거나 커밋 메시지를 작성할 때마다 별다른 고민 없이 일관된 형식으로 작성할 수 있게 됐다. 또한, 이슈와 PR도 모두 같은 형식으로 관리되어 리뷰 시에는 형식보다 내용이라는 핵심에 집중할 수 있어 훨씬 효율적이다.</p>
<p>다만, 컨벤션이라고 해서 고정된 것은 아니다. 프로젝트가 진행되면서 팀 회의를 통해 꾸준히 업그레이드할 예정이다. 예를 들어, 원래는 main과 dev만 있어서 이슈 단위로 작업한 브랜치를 바로 dev로 보냈다. 하지만 지금은 이슈 단위로 작업한 브랜치를 기능별 브랜치에 모은 뒤, dev에 병합하는 방식을 제안하면서 기능 브렌치도 추가하게 되었다.</p>
<hr>
<h1 id="1️⃣-커밋-컨벤션">1️⃣ 커밋 컨벤션</h1>
<h2 id="커밋-타입type">커밋 타입(Type)</h2>
<ul>
<li>feat: 새로운 기능 추가</li>
<li>fix: 버그 수정</li>
<li>docs: 문서 수정</li>
<li>style: 코드 포맷/세미콜론 등 비기능적 변경</li>
<li>refactor: 코드 리팩토링</li>
<li>test: 테스트 코드 추가/수정</li>
<li>chore: 빌드, 패키지 등 기타 변경</li>
</ul>
<h2 id="커밋-구조">커밋 구조</h2>
<pre><code>Type: 버튼 컴포넌트 구현

Body: (커밋에 대한 상세 설명)</code></pre><h2 id="커밋-작성-규칙">커밋 작성 규칙</h2>
<ul>
<li>타입(Type)은 영어 소문자로 작성한다.</li>
<li>Type:과 Body: 뒤에 한 칸 공백을 둔다.</li>
<li>제목(Subject)은 최대 50자 이내로 간결하게 작성한다.</li>
<li>한글로 작성하며 -하기 / -삭제 / -구현 / -추가 형태로 끝낸다.</li>
<li>끝에 마침표 및 특수기호는 금지한다.</li>
<li>Subject와 Body 사이에 한 줄 개행한다.</li>
</ul>
<h2 id="커밋-예시">커밋 예시</h2>
<pre><code>feat: 회원 가입 기능 구현

- SMS, 이메일 중복확인 API 개발
- 웹 클라이언트 사용 방식에서 서버 통신 방식으로 변경</code></pre><h1 id="2️⃣-브렌치-전략">2️⃣ 브렌치 전략</h1>
<h2 id="브렌치-종류">브렌치 종류</h2>
<table>
<thead>
<tr>
<th>브랜치</th>
<th>역할 및 용도</th>
<th>유지 기간</th>
</tr>
</thead>
<tbody><tr>
<td><strong>main</strong></td>
<td>실제 서비스용 브랜치(항상 안정적인 코드 유지)</td>
<td>항상 존재</td>
</tr>
<tr>
<td><strong>dev</strong></td>
<td>여러 기능을 통합·테스트하는 공용 개발 브랜치</td>
<td>항상 존재</td>
</tr>
<tr>
<td><strong>feat</strong></td>
<td>개별 기능 개발용 임시 브랜치</td>
<td>기능 개발 시 생성, 완료 후 삭제</td>
</tr>
</tbody></table>
<hr>
<h2 id="브랜치-네이밍-규칙">브랜치 네이밍 규칙</h2>
<ul>
<li><strong>공통 기능 개발</strong></li>
</ul>
<pre><code class="language-js">파트명/feat-기능명

ex) fe/feat-login-api
ex) be/feat-common-component</code></pre>
<ul>
<li><strong>개인 작업</strong></li>
</ul>
<pre><code class="language-js">feat#이슈번호  

ex) feat#123
ex) feat#456</code></pre>
<h2 id="브랜치-사용-흐름-예시">브랜치 사용 흐름 예시</h2>
<pre><code>main                ← 배포용 (완전 안정)
└─ dev              ← 공용 개발 브랜치
    ├─ fe/feat-login-api
    └─ be/feat-common-component ← 기능 브랜치
                  ├─ feat#123     ← 개인 작업 브랜치
                  └─ feat#125     ← 개인 작업 브랜치</code></pre><h2 id="브랜치-권한">브랜치 권한</h2>
<ul>
<li><strong>main</strong><ul>
<li>최소 2명이상 승인해야 Merge 가능</li>
</ul>
</li>
<li><strong>dev</strong><ul>
<li>최소 1명이상 승인해야 Merge 가능</li>
</ul>
</li>
<li><strong>파트명/feat-기능명</strong><ul>
<li>최소 1명이상 승인해야 Merge 가능</li>
</ul>
</li>
</ul>
<hr>
<h1 id="3️⃣-이슈-템플릿">3️⃣ 이슈 템플릿</h1>
<h2 id="기능-개발">기능 개발</h2>
<pre><code class="language-js">name: &quot;💡 Feat&quot;
description: &quot;새로운 기능 추가 템플릿&quot;
title: &quot;[Feat] &quot;
labels: &quot;feat&quot;
body:
  - type: textarea
    attributes:
      label: 📝 설명
      description: 새로운 기능에 대해 설명해주세요.
      placeholder: 기능에 대해서 적어주세요.
    validations:
      required: true
  - type: textarea
    attributes:
      label: ✅ 체크사항
      description: 주어진 기능에 대해서 체크박스를 만들어주세요.
      placeholder: 기능에 대해서 나열해주세요.
    validations:
      required: true</code></pre>
<h2 id="버그-수정">버그 수정</h2>
<pre><code class="language-js">name: &quot;🛠️ Fix&quot;
description: &quot;Fix 템플릿&quot;
title: &quot;[Fix] &quot;
labels: &quot;fix&quot;
body:
  - type: textarea
    attributes:
      label: 🐞 버그 설명
      description: 버그에 대한 설명을 작성해 주세요.
    validations:
      required: true
  - type: textarea
    attributes:
      label: 🧾 로그
      description: 로그가 있으면 복붙해 주세요.
      render: shell
    validations:
      required: false</code></pre>
<h2 id="리펙토링">리펙토링</h2>
<pre><code class="language-js">name: &quot;♻️ Refactor&quot;
description: &quot;리팩토링 템플릿&quot;
title: &quot;[Refactor] &quot;
labels: &quot;refactor&quot;
body:
  - type: textarea
    attributes:
      label: ❓ 이유
      description: 리팩토링을 진행한 이유를 설명해주세요.
    validations:
      required: true
  - type: textarea
    attributes:
      label: ✅ 수정
      description: 수정한 부분을 적어주세요.
    validations:
      required: true</code></pre>
<h1 id="4️⃣-pr-템플릿">4️⃣ PR 템플릿</h1>
<pre><code class="language-js">## #️⃣ Issue Number


## 🕹️ 작업 내용

한 줄 요약 : 
- [ ]
- [ ]

## 📋 리뷰 포인트

-
-

## 🔮 기타 사항

-
-</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모잇지] 바닥부터 Webpack 환경 설정하기]]></title>
            <link>https://velog.io/@kaori-killer/%EB%AA%A8%EC%9E%87%EC%A7%80-%EB%B0%94%EB%8B%A5%EB%B6%80%ED%84%B0-Webpack-%ED%99%98%EA%B2%BD-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@kaori-killer/%EB%AA%A8%EC%9E%87%EC%A7%80-%EB%B0%94%EB%8B%A5%EB%B6%80%ED%84%B0-Webpack-%ED%99%98%EA%B2%BD-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 13 Jul 2025 09:10:12 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/kaori-killer/post/0901e5e8-3359-47be-8bc1-da998da35ea1/image.png" alt=""></p>
<h1 id="👋-들어가며">👋 들어가며</h1>
<p>레벨 3 팀 프로젝트에서는 번들러로 Webpack을 사용해야 한다.<br>그래서 직접 환경 설정을 해보면서 과정을 정리해봤다.</p>
<hr>
<h2 id="1️⃣-webpack을-사용하는-이유">1️⃣ Webpack을 사용하는 이유</h2>
<blockquote>
<p>Webpack에 대한 이해가 있다면 대부분의 나머지 빌드 도구는 이해가 어렵지 않을 것으로 생각합니다.</p>
<p>Vite 등의 다른 도구를 도입하는 경우가 늘고는 있으나, 아직까지는 프로덕션에서 Webpack 기반으로 이미 구성된 프로젝트가 많은 것으로 판단하였습니다. 이 도구를 사용해 빌드 환경을 직접 구성하고, 프로젝트를 진행하는 과정에서의 트러블 슈팅 경험을 쌓아보기를 권장합니다.</p>
</blockquote>
<hr>
<h2 id="2️⃣-요구사항">2️⃣ 요구사항</h2>
<ul>
<li><input disabled="" type="checkbox"> Webpack 기반의 React &amp; TypeScript 프로젝트 구조가 구성되어 있고, 실행 및 빌드가 가능하다.</li>
<li><input disabled="" type="checkbox"> Webpack 기반의 프론트엔드 프로젝트를 직접 설정한다.</li>
<li><input disabled="" type="checkbox"> 프론트엔드 리소스 빌드 및 번들링에 필요한 요소를 이해하고 설정할 수 있다.</li>
</ul>
<hr>
<h1 id="🔧-webpack-환경-설정을-하자">🔧 Webpack, 환경 설정을 하자</h1>
<p>공식문서를 보면 환경 설정을 할 수 있는 방법은 2가지다.<br>모두 해봤고 비교해봤다.</p>
<ol>
<li>create 명령어를 이용해서 간단하게 설치하기<ul>
<li><a href="https://webpack.kr/configuration/">공식문서: Set up a new webpack project</a></li>
<li><a href="https://github.com/kaori-killer/playground/tree/wepack-setting-create-mode">GitHub에 올린 환경 설정한 레포</a></li>
</ul>
</li>
<li>직접 하나하나 설치하기<ul>
<li><a href="https://webpack.kr/guides/getting-started">공식문서: getting-started</a></li>
<li><a href="https://github.com/kaori-killer/playground/tree/webpack-setting-no-babel">GitHub에 올린 환경 설정한 레포</a></li>
</ul>
</li>
</ol>
<hr>
<h2 id="1️⃣-create-명령어를-이용해서-간단하게-설치하기">1️⃣ create 명령어를 이용해서 간단하게 설치하기</h2>
<ul>
<li>⏱ 소요시간: 5분</li>
<li>장점<ul>
<li>초기 환경 설정이 없는 상태라면, create 명령어를 이용해서 빠르게 프로젝트를 만들 수 있다.</li>
<li>TypeScript 같은 부가적인 도구도 함께 사용할 수 있도록 도와줘서, 따로 설치하거나 Webpack에 해당 도구를 사용한다고 설정할 필요가 없다. 알아서 해준다.</li>
</ul>
</li>
<li>단점<ul>
<li>직접 작성하지 않은 코드가 작성된다. 내가 필요한 것보다 더 많은 설정이 되어 있을 수도 있다.</li>
<li>나중에 설정 오류가 발생하면 파악하는데 시간이 걸린다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="2️⃣-직접-하나하나-설치하기">2️⃣ 직접 하나하나 설치하기</h2>
<ul>
<li>⏱ 소요시간: 30분</li>
<li>장점<ul>
<li>필요한 부분만 설정에 포함하면서 하나씩 쌓아갈 수 있다.</li>
<li>환경에 대한 이해도가 올라간다.</li>
</ul>
</li>
<li>단점<ul>
<li>TypeScript 같은 추가적인 도구를 쓰려면 따로 설치 및 설정을 해줘야 한다. 다시 말해서, 도구를 설치하고 Webpack에 해당 도구를 사용한다고 설정해야 한다.</li>
</ul>
</li>
</ul>
<hr>
<h1 id="⭐️-webpack-환경-설정하며-겪은-과정-정리">⭐️ Webpack 환경 설정하며 겪은 과정 정리</h1>
<p>직접 환경 설정(2번 방법)을 해보면서 여러 오류를 마주했고, 그때마다 어떻게 문제를 정의하고 해결했는지 과정을 정리했다.  </p>
<h3 id="💫-referenceerror-require-is-not-defined-in-es-module-scope">💫 ReferenceError: require is not defined in ES module scope</h3>
<pre><code>[webpack-cli] Failed to load &#39;/Users/.../webpack.config.js&#39; config
[webpack-cli] ReferenceError: require is not defined in ES module scope, you can use import instead
This file is being treated as an ES module because it has a &#39;.js&#39; file extension and &#39;package.json&#39; contains &quot;type&quot;: &quot;module&quot;. 
To treat it as a CommonJS script, rename it to use the &#39;.cjs&#39; file extension.</code></pre><ul>
<li><p>문제<br>webpack.config.js 파일에서 require를 사용했는데, 내 프로젝트는 &quot;type&quot;: &quot;module&quot;로 설정되어 있어 CommonJS 문법이 지원되지 않는 상황이었다. (공식문서에 webpack.config.js 파일이 CommonJS로 작성되어 있었음.)</p>
</li>
<li><p>가설</p>
<ol>
<li>package.json에서 &quot;type&quot;: &quot;module&quot;을 제거해 CommonJS 환경으로 돌린다.  </li>
<li>webpack.config.js를 ESM 문법으로 변경한다.</li>
</ol>
</li>
<li><p>해결<br>나는 앞으로도 ESM 환경을 유지할 생각이라 2번 방법을 선택했다.  </p>
</li>
</ul>
<pre><code class="language-js">// webpack.config.js

import path from &#39;path&#39;;
import { fileURLToPath } from &#39;url&#39;;

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export default {
  entry: &#39;./src/index.tsx&#39;,
  output: {
    filename: &#39;main.js&#39;,
    path: path.resolve(__dirname, &#39;dist&#39;),
  },
};</code></pre>
<hr>
<h3 id="💫-the-mode-option-has-not-been-set">💫 The &#39;mode&#39; option has not been set</h3>
<pre><code>WARNING in configuration
The &#39;mode&#39; option has not been set, webpack will fallback to &#39;production&#39; for this value.</code></pre><ul>
<li><p>문제<br>mode 옵션이 빠져 있어서 기본값 production으로 동작하고 있었다.</p>
</li>
<li><p>가설<br>webpack.config.js에 mode: &quot;development&quot; 또는 mode: &quot;production&quot;을 명시한다.</p>
</li>
<li><p>해결<br>mode를 추가해 해결!  </p>
</li>
</ul>
<pre><code class="language-js">export default {
  entry: &#39;./src/index.tsx&#39;,
  mode: &#39;production&#39;, // mode 설정
  output: {
    filename: &#39;main.js&#39;,
    path: path.resolve(__dirname, &#39;dist&#39;),
  },
};</code></pre>
<hr>
<h3 id="💫-you-may-need-an-appropriate-loader-to-handle-this-file-type-1">💫 You may need an appropriate loader to handle this file type (1)</h3>
<pre><code>ERROR in ./src/index.tsx 5:51
Module parse failed: Unexpected token (5:51)
You may need an appropriate loader to handle this file type...</code></pre><ul>
<li><p>문제<br>적절한 loader가 없어서 .tsx 파일을 해석하지 못하는 상황이었다.</p>
</li>
<li><p>가설<br><a href="https://webpack.kr/concepts/#plugins">에러 메시지에 있는 Webpack 가이드</a>에 나온 대로 html-webpack-plugin을 추가해본다.<br>이 플러그인은 HTML 파일을 자동 생성하고, 빌드된 JS를 삽입해준다.  </p>
</li>
<li><p>해결<br>플러그인을 설치하고 설정했다.  </p>
</li>
</ul>
<pre><code class="language-bash">npm install --save-dev html-webpack-plugin</code></pre>
<pre><code class="language-js">import HtmlWebpackPlugin from &#39;html-webpack-plugin&#39;;

export default {
  plugins: [
    new HtmlWebpackPlugin({
      template: &#39;./src/index.html&#39;,
    }),
  ],
};</code></pre>
<blockquote>
<p>✅ html-webpack-plugin은 왜 쓰나?</p>
<p>Webpack으로 번들링을 하면 main.js 같은 결과물이 나오지만, 이 파일을 직접 HTML에 <code>&lt;script src=&quot;main.js&quot;&gt;</code>로 넣어줘야 브라우저가 실행할 수 있다. 그런데 실제 프로젝트에서는 빌드할 때마다 번들 파일 이름이 바뀌거나 해시값(main.9f8a7b.js)이 붙는 경우가 많아 일일이 HTML을 수정하기 번거롭다. html-webpack-plugin은 이런 과정을 자동화해준다. 템플릿 HTML을 기반으로 최신 JS/CSS 파일을 자동으로 삽입해주고, 빌드할 때마다 새로운 파일명을 알아서 반영하기 때문에 개발자 입장에서 따로 신경 쓸 필요가 없다.</p>
</blockquote>
<hr>
<h3 id="💫-you-may-need-an-appropriate-loader-to-handle-this-file-type-2">💫 You may need an appropriate loader to handle this file type (2)</h3>
<ul>
<li><p>문제<br>위 플러그인을 추가했는데도 여전히 같은 오류 발생.<br>TypeScript 파일이라서 적절한 loader가 필요했다.</p>
</li>
<li><p>가설<br><a href="https://webpack.kr/guides/typescript/">ts-loader를 추가하면 .tsx 파일을 처리</a>할 수 있을 것이다.  </p>
</li>
<li><p>해결<br>TypeScript 관련 설정을 추가했다.  </p>
</li>
</ul>
<pre><code class="language-bash">npm install --save-dev ts-loader typescript</code></pre>
<pre><code class="language-js">export default {
  module: {
    rules: [
      { test: /\.tsx?$/, use: &#39;ts-loader&#39;, exclude: /node_modules/ },
    ],
  },
};</code></pre>
<blockquote>
<p>✅ Webpack만 단독으로 쓰면?</p>
<p>Webpack은 기본적으로 번들러다. 여러 JS 파일을 하나로 합쳐서 브라우저가 읽을 수 있는 파일로 만들어주는 역할을 한다. 하지만 이때 TypeScript(.ts, .tsx) 파일은 처리하지 못한다. 왜냐하면 Webpack은 JavaScript만 이해하기 때문이다. TypeScript를 그대로 번들링하려 하면 에러가 발생하고 멈춘다. 그래서 TypeScript를 JavaScript로 변환해주는 &#39;트랜스파일러&#39;가 필요하다.</p>
</blockquote>
<blockquote>
<p>✅ ts-loader는 뭘 해주나?</p>
<p>이 역할을 하는 게 바로 ts-loader다. ts-loader는 Webpack이 TypeScript 코드를 이해하고 번들링할 수 있도록 도와주는 Webpack 로더다. 내부적으로는 TypeScript 컴파일러(tsc)를 호출해 두 가지 일을 한다:</p>
<ol>
<li>트랜스파일링 – TypeScript를 JavaScript로 변환</li>
<li>타입 검사 – 코드에 타입 에러가 없는지 검증
즉, ts-loader는 Webpack의 로더이면서 동시에 TypeScript 변환기(트랜스파일러) 역할도 한다.</li>
</ol>
</blockquote>
<blockquote>
<p>✅ 그럼 Babel은?</p>
<p>꼭 ts-loader만 써야 하는 건 아니다. Babel도 사용할 수 있다. Babel은 원래 최신 JavaScript(ES6+) 문법을 구버전 JS로 변환하는 도구인데, 여기에 @babel/preset-typescript 플러그인을 추가하면 TypeScript 문법도 제거할 수 있다. 하지만 Babel은 타입 검사를 하지 않고, 문법만 제거해서 JS로 바꾼다. 타입 에러가 있어도 그냥 넘어간다는 점이 ts-loader와의 중요한 차이다.</p>
</blockquote>
<blockquote>
<p>✅ ts-loader와 Babel을 같이 쓸 수도 있다</p>
<p>ts-loader로는 타입 검사만 하고(transpileOnly: true), Babel로는 최신 문법 변환과 polyfill 처리를 맡기는 방식이다. 이렇게 하면 타입 안정성과 빠른 빌드 속도를 동시에 잡을 수 있다. (ts-loader로 타입 검사와 트렌스파일링을 모두 하게 되면 빌드 속도가 느릴 수 있기 때문이다.)</p>
</blockquote>
<hr>
<h3 id="💫-you-need-to-install-webpack-dev-server-package">💫 you need to install: &#39;webpack-dev-server&#39; package</h3>
<pre><code>[webpack-cli] For using &#39;serve&#39; command you need to install: &#39;webpack-dev-server&#39; package.</code></pre><ul>
<li><p>문제<br>webpack-dev-server가 없어 npx webpack serve 명령어가 동작하지 않았다.</p>
</li>
<li><p>가설<br>webpack-dev-server를 설치하면 해결될 것 같다.</p>
</li>
<li><p>해결<br>설치 후 바로 정상 작동.  </p>
</li>
</ul>
<pre><code class="language-bash">npm install --save-dev webpack-dev-server</code></pre>
<blockquote>
<p>✅ webpack-dev-server는 왜 쓰나?</p>
<p>webpack-dev-server는 개발 중 코드가 바뀔 때마다 자동으로 번들링하고 브라우저를 새로고침해주는 개발용 서버다. Webpack으로만 작업하면 코드가 바뀔 때마다 새 파일을 만들고 저장소에 저장한 뒤, 브라우저가 그 파일을 다시 불러와야 한다. 이 과정이 느릴 수 있는데, webpack-dev-server는 번들 파일을 저장소에 파일로 쓰지 않고 컴퓨터의 메모리에만 잠깐 올려서 브라우저에 바로 전달하기 때문에 훨씬 빠르다.</p>
</blockquote>
<hr>
<h1 id="🍵-마무리">🍵 마무리</h1>
<p>이전 미션에서는 Vite만 써왔던 터라, Webpack을 설치해보니 설정할 것이 더 많다는 걸 느꼈다. 덕분에 오류도 계속 발생했고  단순히 초기 세팅을 넘어서, 오류가 발생했을 때 문제를 정의하고 가설을 세우고 해결해나가는 사이클을 경험할 수 있었다. 개인적으로는 React 코드에서 발생하는 에러보다 이런 환경 설정 오류를 해결하는 과정이 더 어렵게 느껴졌다.</p>
<p>추가로,
Webpack 외의 도구는 <a href="https://velog.io/@kaori-killer/%EB%82%98%EC%9D%98-%EB%AA%A8%EB%8B%A5%EB%B6%88-%EB%B0%94%EB%8B%A5%EB%B6%80%ED%84%B0-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0">TypeScript, ESLint, Prettier, React를 설치하는 가이드</a> 문서를 참고해서 설치했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모잇지] 스타일링 도구로 emotion을 선택한 이유는?]]></title>
            <link>https://velog.io/@kaori-killer/%EB%AA%A8%EC%9E%87%EC%A7%80-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81-%EB%8F%84%EA%B5%AC%EB%A1%9C-emotion%EC%9D%84-%EC%84%A0%ED%83%9D%ED%95%9C-%EC%9D%B4%EC%9C%A0%EB%8A%94</link>
            <guid>https://velog.io/@kaori-killer/%EB%AA%A8%EC%9E%87%EC%A7%80-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81-%EB%8F%84%EA%B5%AC%EB%A1%9C-emotion%EC%9D%84-%EC%84%A0%ED%83%9D%ED%95%9C-%EC%9D%B4%EC%9C%A0%EB%8A%94</guid>
            <pubDate>Sat, 12 Jul 2025 07:14:02 GMT</pubDate>
            <description><![CDATA[<h1 id="스타일링">스타일링</h1>
<h2 id="✅-요구사항">✅ 요구사항</h2>
<p>우테코 스타일링 요구사항이다.</p>
<ol>
<li><input disabled="" type="checkbox"> 다양한 스타일링 방법의 특징과 차이를 이해한다.</li>
<li><input disabled="" type="checkbox"> 직접 스타일링 할 수 있는 방법 중 한 가지를 팀 내에서 자율적으로 결정하고 도입한다.</li>
<li><input disabled="" type="checkbox"> 프로젝트 상황에 적합한 방법이다.</li>
<li><input disabled="" type="checkbox"> 결정한 이유가 문서화한다.</li>
<li><input disabled="" type="checkbox"> 기술적 선택의 이유를 설명할 수 있다.</li>
</ol>
<h2 id="✅-사용-가능한-도구는">✅ 사용 가능한 도구는?</h2>
<p>Bootstrap, MUI, Ant Design, Chakra UI, Tailwind CSS 등은 금지된다. 실제 프로덕트의 기획과 디자인은 이런 틀만으로 만족시킬 수 없는 경우가 많기 때문이다. 사용하더라도 서비스의 필요에 맞춰 커스텀이 필요한 일이 잦다고 한다. 그래서 이번 미션에서는 직접 마크업하고 스타일링 하는 기본기를 쌓아두는 것을 목표로 한다.</p>
<ul>
<li>CSS</li>
<li>CSS Modules</li>
<li>CSS-in-JS (Emotion 등)</li>
<li>Zero Runtime CSS-in-JS (Vanilla extract 등)</li>
<li>CSS Preprocessor (SASS 등)</li>
</ul>
<h2 id="🤔-경험을-바탕으로-비교해보면">🤔 경험을 바탕으로 비교해보면...</h2>
<p>일단 경험을 바탕으로 CSS, CSS Modules, Emotion을 비교해보면...</p>
<ul>
<li><strong>CSS</strong><ul>
<li>👍 설치 필요 없음</li>
<li>❌ 글로벌 범위라 클래스명 충돌 관리해야 함</li>
</ul>
</li>
<li><strong>CSS Modules</strong><ul>
<li>👍 클래스명 충돌 해결 (자동으로 해시 처리함)</li>
<li>❌ props 기반 동적 스타일링이 불편함 (조건부 스타일링은 가능하지만, css Modules는 정적 클래스 기반이라 props를 스타일 쪽에서  직접 받아서 처리하는 건 어려움)</li>
</ul>
</li>
<li><strong>Emotion/styled</strong><ul>
<li>🤔 스타일과 컴포넌트가 완전히 묶임</li>
<li>👍 props를 바로 받아서 조건부 처리가 가능</li>
<li>❌ 스타일 재사용이 어려움</li>
</ul>
</li>
<li><strong>Emotion/react</strong><ul>
<li>🤔 스타일과 컴포넌트가 묶여 있지 않음</li>
<li>👍 여러 컴포넌트가 하나의 스타일 재사용 가능</li>
<li>❌ props 값을 스타일 함수 파아미터로 전달해서 조건부 처리(props를 스타일 함수로 일일이 전달해야 해서 귀찮음)</li>
</ul>
</li>
</ul>
<h2 id="🤔-런타임-시간을-생각해보면">🤔 런타임 시간을 생각해보면...</h2>
<ul>
<li><strong>CSS, CSS Modules</strong><ul>
<li>✅ 런타임 비용 없음</li>
<li>스타일을 빌드 타임에 미리 계산해서 .css 파일로 만들어 브라우저에 전달함</li>
<li>앱 실행 시점에는 브라우저가 CSS 파일만 읽고 바로 스타일 적용함</li>
<li>JS 코드와 CSS가 완전히 분리됨</li>
<li>처리 흐름: <code>[개발] → CSS 파일 생성 → 브라우저가 파일을 읽음</code>
→ 빠르고 가볍다. 앱 규모가 커져도 스타일링 성능 이슈 거의 없다.</li>
</ul>
</li>
<li><strong>Emotion</strong><ul>
<li>⚠️ 런타임 비용 있음</li>
<li>CSS-in-JS 방식 → CSS를 JS 코드 안에서 작성함</li>
<li>props/state 값에 따라 JS 코드 실행 시점에 스타일 계산함</li>
<li>계산된 CSS를 기반으로 고유한 클래스명 생성함 (ex: css-abc123)</li>
<li>DOM에 <code>&lt;style&gt;</code> 태그를 동적으로 삽입함</li>
<li>처리 흐름: <code>[앱 실행 시] → JS 코드 실행 → props 값 기반으로 CSS 계산 → &lt;style&gt; 태그 생성 → DOM에 삽입</code>
→ 동적 스타일링은 강력하지만,
→ 스타일 계산 및 삽입 때문에 초기 렌더링에서 비용 발생 할 수 있다.
→ 다만 <code>@emotion/babel-plugin</code> 등으로 최적화 가능하다.</li>
</ul>
</li>
</ul>
<p>라이브러리에 따라 런타임 시간 유무가 있다. 하지만 런타임 시간은 충분히 줄일 수 있어서 크게 단점이 되는 것 같지 않다.</p>
<h2 id="🤔-사용자-스토리-작성으로-비교하기">🤔 사용자 스토리 작성으로 비교하기?</h2>
<p>Spotify 팀은 스타일링 도구를 선정할 때 사용자 스토리와 기능을 정의하고, 각 항목별로 객관적인 점수를 매겨 결정하는 POS 과정을 거친다고 한다. <a href="https://changelog.com/jsparty/190">(링크)</a></p>
<p>그래서 나도 이전 미션에서의 경험을 가지고 스타일링 도구를 비교해보았다.
기능이 존재하지 않으면 0점, 기술 부채를 유발하는 경우에는 1점, 완벽하게 제공되면 2점이다.</p>
<ul>
<li>사용자 스토리 (기능)</li>
<li>HTML 코드를 보면 대략 어떤 스타일인지 알 수 있기를 바란다. (직관적 이해)</li>
<li>클래스명을 정하는 데 너무 많은 시간 쓰지 않길 바란다. (CSS 네이밍 부담 최소화)</li>
<li>CSS를 보기 위해 파일 위아래를 왔다갔다 하지 않도록 하고 싶다. (CSS 위치 접근성)</li>
<li>HTML 용량을 줄이기 위해 클래스명을 난독화하고 싶다. (production에서 클래스명 난독화)</li>
<li>타입스크립트로 값을 올바르게 사용하는 데 자신감을 갖고 싶다. (타입스크립트 지원)</li>
<li>자세하고 최신화된 공식 문서를 원한다. (문서화)</li>
<li>다양한 조직에서 검증된 프레임워크를 사용하고 싶다. (커뮤니티 해결 사례)</li>
<li>디자인 시스템 값에 대한 자동완성이 있으면 좋겠다. (VSC 자동완성)</li>
<li>색상 미리 보기 등 VSCode에서 추가 도구를 사용할 수 있으면 좋겠다. (도구 확장성)</li>
<li>배운 기술이 다른 사이드 프로젝트나 직장에서 그대로 쓸 수 있으면 좋겠다. (기술 이전 가능성)</li>
</ul>
<h2 id="🤔-사용자-스토리-작성으로-비교하기-1">🤔 사용자 스토리 작성으로 비교하기!</h2>
<h3 id="1️⃣-사용자-스토리-html-코드를-보면-스타일-직관적-이해">1️⃣ 사용자 스토리: HTML 코드를 보면 스타일 직관적 이해</h3>
<table>
<thead>
<tr>
<th>기술</th>
<th>점수</th>
<th>이유</th>
</tr>
</thead>
<tbody><tr>
<td>CSS</td>
<td>2</td>
<td>✅ 클래스명 그대로 보여서 구조 파악 쉬움</td>
</tr>
<tr>
<td>CSS Modules</td>
<td>2</td>
<td>✅ 해시 클래스지만 JS import로 추적 가능</td>
</tr>
<tr>
<td>Emotion/css</td>
<td>1</td>
<td>⚠️ 클래스명이 난독화(<code>css-abc123</code>) → 직관성 조금 떨어짐</td>
</tr>
<tr>
<td>Emotion/styled</td>
<td>0</td>
<td>❌ DOM에 StyledComponent로 표시 → HTML에서 스타일 추적 어려움</td>
</tr>
</tbody></table>
<h3 id="2️⃣-사용자-스토리-css-네이밍-부담-최소화">2️⃣ 사용자 스토리: CSS 네이밍 부담 최소화</h3>
<table>
<thead>
<tr>
<th>기술</th>
<th>점수</th>
<th>이유</th>
</tr>
</thead>
<tbody><tr>
<td>CSS</td>
<td>0</td>
<td>❌ 글로벌 스코프라 네이밍 전략(BEM 등) 필요</td>
</tr>
<tr>
<td>CSS Modules</td>
<td>2</td>
<td>✅ 자동 해시 처리 → 네이밍 충돌 완벽 방지</td>
</tr>
<tr>
<td>Emotion/css</td>
<td>2</td>
<td>✅ 해시 클래스 생성으로 네이밍 부담 없음</td>
</tr>
<tr>
<td>Emotion/styled</td>
<td>2</td>
<td>✅ 해시 클래스 생성으로 네이밍 부담 없음</td>
</tr>
</tbody></table>
<h3 id="3️⃣-사용자-스토리-css-위치-접근성-파일-위아래-이동-필요">3️⃣ 사용자 스토리: CSS 위치 접근성 (파일 위아래 이동 필요)</h3>
<table>
<thead>
<tr>
<th>기술</th>
<th>점수</th>
<th>이유</th>
</tr>
</thead>
<tbody><tr>
<td>CSS</td>
<td>0</td>
<td>❌ CSS 파일과 JS가 분리돼있어 코드 확인 시 파일 이동 필요</td>
</tr>
<tr>
<td>CSS Modules</td>
<td>1</td>
<td>⚠️ 컴포넌트 단위로 관리 가능하지만 여전히 CSS 파일 따로 관리</td>
</tr>
<tr>
<td>Emotion/css</td>
<td>2</td>
<td>✅ 스타일을 JS 코드와 같이 작성 → 파일 이동 불필요</td>
</tr>
<tr>
<td>Emotion/styled</td>
<td>2</td>
<td>✅ 스타일과 컴포넌트 완전 결합 → 한곳에서 관리 가능</td>
</tr>
</tbody></table>
<h3 id="4️⃣-사용자-스토리-프로덕션-클래스명-난독화">4️⃣ 사용자 스토리: 프로덕션 클래스명 난독화</h3>
<table>
<thead>
<tr>
<th>기술</th>
<th>점수</th>
<th>이유</th>
</tr>
</thead>
<tbody><tr>
<td>CSS</td>
<td>0</td>
<td>❌ 빌드 시 클래스명이 그대로 노출</td>
</tr>
<tr>
<td>CSS Modules</td>
<td>2</td>
<td>✅ 클래스명이 해시 처리(<code>Button_button__abc123</code>)로 난독화됨</td>
</tr>
<tr>
<td>Emotion/css</td>
<td>2</td>
<td>✅ 클래스명이 해시 처리(<code>css-xyz456</code>)로 난독화됨</td>
</tr>
<tr>
<td>Emotion/styled</td>
<td>2</td>
<td>✅ 클래스명이 해시 처리(<code>css-xyz456</code>)로 난독화됨</td>
</tr>
</tbody></table>
<h3 id="5️⃣-사용자-스토리-타입스크립트-지원">5️⃣ 사용자 스토리: 타입스크립트 지원</h3>
<table>
<thead>
<tr>
<th>기술</th>
<th>점수</th>
<th>이유</th>
</tr>
</thead>
<tbody><tr>
<td>CSS</td>
<td>0</td>
<td>❌ TS와 연계된 기능 없음</td>
</tr>
<tr>
<td>CSS Modules</td>
<td>1</td>
<td>⚠️ 클래스명 타입 추론 가능하지만 CSS 속성 자동완성 불가</td>
</tr>
<tr>
<td>Emotion/css</td>
<td>2</td>
<td>✅ props 타입 추론과 자동완성 지원</td>
</tr>
<tr>
<td>Emotion/styled</td>
<td>2</td>
<td>✅ props 타입 추론과 자동완성 지원</td>
</tr>
</tbody></table>
<h3 id="6️⃣-사용자-스토리-디자인-시스템-자동완성">6️⃣ 사용자 스토리: 디자인 시스템 자동완성</h3>
<table>
<thead>
<tr>
<th>기술</th>
<th>점수</th>
<th>이유</th>
</tr>
</thead>
<tbody><tr>
<td>CSS</td>
<td>0</td>
<td>❌ props 기반 스타일링 불가, 자동완성 미지원</td>
</tr>
<tr>
<td>CSS Modules</td>
<td>1</td>
<td>⚠️ 제한적 지원 (조건부 클래스 처리만 가능)</td>
</tr>
<tr>
<td>Emotion/css</td>
<td>2</td>
<td>✅ props 기반 동적 스타일링 지원 → 디자인 시스템에 최적화</td>
</tr>
<tr>
<td>Emotion/styled</td>
<td>2</td>
<td>✅ props 기반 스타일링 자연스럽게 지원</td>
</tr>
</tbody></table>
<h3 id="7️⃣-사용자-스토리-기술-이전-가능성-다른-프로젝트-적용">7️⃣ 사용자 스토리: 기술 이전 가능성 (다른 프로젝트 적용)</h3>
<table>
<thead>
<tr>
<th>기술</th>
<th>점수</th>
<th>이유</th>
</tr>
</thead>
<tbody><tr>
<td>CSS</td>
<td>2</td>
<td>✅ CSS는 모든 프로젝트 기본 포함 → 이전 용이</td>
</tr>
<tr>
<td>CSS Modules</td>
<td>2</td>
<td>✅ React 생태계에서 많이 사용 → 다른 프로젝트 적용 용이</td>
</tr>
<tr>
<td>Emotion/css</td>
<td>2</td>
<td>✅ CSS-in-JS 지원하는 프로젝트로 이전 가능</td>
</tr>
<tr>
<td>Emotion/styled</td>
<td>2</td>
<td>✅ CSS-in-JS 지원하는 프로젝트로 이전 가능</td>
</tr>
</tbody></table>
<h3 id="✅-최종-점수">✅ <strong>최종 점수</strong></h3>
<table>
<thead>
<tr>
<th>기술</th>
<th>점수</th>
<th>총평</th>
</tr>
</thead>
<tbody><tr>
<td><strong>CSS</strong></td>
<td>10</td>
<td>기본적인 스타일링에 적합. 글로벌 충돌 관리 필요</td>
</tr>
<tr>
<td><strong>CSS Modules</strong></td>
<td>17</td>
<td>클래스 충돌 방지 및 정적 스타일링 강점. props 기반 동적 스타일은 불편</td>
</tr>
<tr>
<td><strong>Emotion/css</strong></td>
<td>21</td>
<td>디자인 시스템 구축에 최적. props 기반 동적 스타일링 자연스럽고 재사용성 높음</td>
</tr>
<tr>
<td><strong>Emotion/styled</strong></td>
<td>20</td>
<td>빠른 개발에 적합. 스타일과 컴포넌트 결합 → 재사용 설계 시 주의 필요</td>
</tr>
</tbody></table>
<p>Spotify가 중요하게 생각하는 사용자 스토리 중 나에게 필요한 항목만 뽑아 점수를 매겨보니, Emotion이 가장 높은 점수를 기록했다. 특히 props 기반 조건부 스타일링, 타입스크립트 지원, 디자인 시스템 자동완성 등에서 CSS와 CSS Modules와 차이를 보였다. 런타임 비용이라는 단점이 있긴 하지만, 이는 최적화로 해결할 수 있는 부분이고 개발 생산성 측면에서는 Emotion이 가장 적합하다고 판단했다.</p>
<h2 id="☀️-emotionstyled-vs-emotionreact-최종-선택은">☀️ emotion/styled VS emotion/react 최종 선택은?</h2>
<p>이전 미션까지는 컴포넌트 중심으로 빠르게 작업하기 위해 emotion/styled를 주로 사용했다. 하지만 공통된 스타일을 여러 컴포넌트에서 재사용하는 것이 쉽지 않아 점점 불편함을 느꼈다.</p>
<p>그래서 이번에는 Emotion/react를 선택했다. Bootstrap이나 Tailwind CSS처럼 아주 작은 스타일 단위로 그룹을 만들어 클래스로 지정하고, 이를 조합하여 재사용하는 방식에 도전해보려 한다. 이 방식은 유지보수성과 확장성이 높아져, 디자인 시스템을 따라갈 때도 도움이 될 것이라 기대한다.</p>
<h1 id="🍵-마무리">🍵 마무리</h1>
<p>우리가 사용할 디자인 도구를 신중하게 선택했다. 앞으로도 프로젝트를 진행하면서 계속 선택의 순간들을 마주하게 될 것이다. 그 과정에서 더 나은 판단을 내릴 수 있는 선택의 감각을 기르기 위해 여러 방법을 시도하며 경험을 쌓아갈 계획이다 :)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기 FE Lv.3] 20, 21주차 회고]]></title>
            <link>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.3-20-21%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@kaori-killer/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-FE-Lv.3-20-21%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 06 Jul 2025 16:29:33 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/kaori-killer/post/5cfb14da-8539-4d57-a552-75053159f3d3/image.jpg" alt=""></p>
<p>레벨 3로 돌아왔습니다! 😎 (이제는 잠실이다!)</p>
<hr>
<h1 id="🧃-아이디어-착즙-중">🧃 아이디어 착즙 중</h1>
<p>요즘 레벨 3에서 진행할 팀 프로젝트 기획을 하고 있어요. 
아이디어가 쏟아졌는데, 저희 팀은 다음 기준들을 가장 중요하게 봤습니다.</p>
<ul>
<li>페인포인트가 명확한 서비스일 것 (비타민 같은 서비스는 배제)</li>
<li>어플로 구현했을 때 이점이 큰 서비스는 제외</li>
<li>커뮤니티 중심 서비스는 배제</li>
<li>적은 유저 수로도 서비스가 돌아갈 수 있어야 함</li>
<li>주변 사람들에게 제안했을 때 쉽게 쓸 만한 서비스</li>
</ul>
<p>아이디어 중에 &#39;강아지 산책 길 정복&#39;이라는 것도 있었는데요. <del>내가 냄</del>
한 줄로 소개하자면 &quot;동네 산책로를 정복하고, 우리 강아지만의 발자국을 남겨라!&quot;이고,
산책으로 땅따먹기를 하는 컨셉이에요.</p>
<p>이게 팀 투표에서 상위권이었어요. 신선하고 재밌다는 의견도 있었고요.
하지만 다시 저희 기준을 떠올려보니 이건 완전히 비타민형 서비스더라고요.
산책할 때 &quot;아 이거 불편하다!&quot;라는 페인포인트를 명확히 느낀 사람이 거의 없었거든요.
저도 강아지를 키우지만... 있으면 좋고, 없어도 큰 문제 없는 서비스랄까요.
그래서 아쉽지만 배제했습니다.</p>
<p>또 다른 아이디어로는
음악 재생 기능이 있는 서비스였는데,
브라우저에서 구현하면 백그라운드 재생 불가 이슈,
그리고 음악 저작권 문제 때문에 제외하게 되었어요.</p>
<p>이렇게 팀원들과 이야기를 나누다 보니,
아이디어를 여러 시각으로 바라보고, 페인 포인트가 있나? 딱 하나의 핵심 기능만 있어도 사람들이 쓸까? 등을 깊게 고민할 수 있어서 정말 재밌었네요~ </p>
<p><del>근데 머리가 아플 때까지 토론하긴 했어요</del></p>
<h1 id="✨-관심있는-것에-쉽게-시도하기">✨ 관심있는 것에 쉽게 시도하기</h1>
<p>요즘 공부가 재밌어요. <code>playground</code>라는 레포를 만들어서 작고 가볍게 이것저것 실험해보고 있거든요.</p>
<p>지금은 이런 것들을 시도 중입니다.</p>
<ul>
<li>테스트<ul>
<li><input disabled="" type="checkbox"> 스토리북으로 재사용 가능한 컴포넌트 분리</li>
<li><input disabled="" type="checkbox"> UI 레이어를 제외한 부분에 대한 테스트 코드 작성</li>
<li><input disabled="" type="checkbox"> 통합 테스트로 함수와 컴포넌트가 함께 작동하는 방식 검증</li>
</ul>
</li>
<li>도메인 중심 폴더 구조로 구현</li>
<li>웹 접근성</li>
</ul>
<p>사이즈가 작다 보니 부담 없이 시도할 수 있고,
문제가 생겼을 때 어디서 발생했는지 찾기도 쉽더라고요.</p>
<p>예전에는 msw를 설치하다가 에러가 나면, 이게 MSW 자체 문제인지, 아니면 다른 모듈과의 충돌 문제인지 에러 메시지만으로는 정확한 원인을 파악하기 어렵더라고요. 모듈 설정할 때는 에러 메시지가 항상 완전한 원인을 알려주진 않잖아요. 그래서 결국 이것저것 찾아보다가 고치곤 했는데요. 이제는 msw만 따로 떼어서 설치해보고 실험할 수 있게 돼서 훨씬 수월해졌어요.</p>
<p>다음 회고에서는 위 내용에 대해 더 학습한 후, 후기를 남겨볼게요~</p>
<h1 id="🍵-마무리">🍵 마무리...</h1>
<p>저희 팀은 백엔드 4명, 프론트 3명으로 이루어져 있어요. 다들 정말 열정이 넘치고, Figzam에서 브레인스토밍 캔버스를 만들어 주도적으로 이끄는 크루도 있어서 회의가 속전속결이에요.</p>
<p>그리고 무엇보다 다들 경청 능력이 뛰어나서 아직 팀이 된 지 얼마 안 됐지만, 벌써 소소한 감동을 느끼고 있습니다. 얼른 팀원들의 장점을 많이 발견해서 마구마구 칭찬해주고 싶어요. (저도 배우고요!)</p>
<p>레벨 3에서도 어김없이 영어 수업을 시작했는데요, 이제 에런 센세와 좀 더 친해져서 그런지 영어로 더 쉽게, 자신감 있게 말할 수 있게 됐어요. 아직 영어로 동화책 팟캐스트 듣는 건 어렵지만, 이것도 곧 될 거라 믿고 꾸준히 해보려 합니다.</p>
<p>이제 막 시작된 팀 프로젝트
화이팅입니다~! 🚀</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[바닥부터 개발 환경 세팅하기]]></title>
            <link>https://velog.io/@kaori-killer/%EB%B0%94%EB%8B%A5%EB%B6%80%ED%84%B0-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@kaori-killer/%EB%B0%94%EB%8B%A5%EB%B6%80%ED%84%B0-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 30 Jun 2025 08:48:22 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/kaori-killer/post/2d3f611e-ecf2-4e2d-811b-565d8926eb2b/image.png" alt=""></p>
<h1 id="바닥부터-세팅하는-이유는">바닥부터 세팅하는 이유는?</h1>
<p>지금까지 모든 미션은 초기 환경이 이미 세팅된 레포를 받아 시작했다. 그래서 개발 환경을 직접 구축하거나, 어떤 도구를 사용할지 고민해볼 기회가 거의 없었다. 이에 몇 가지 불편함이 있었다. ESLint만 해도 버전이 올라갈수록 설정 방식이 달라지고, 기존 설정과 충돌이 생겼다. 세팅에 대한 기본 이해가 부족하다 보니 문제를 마주했을 때 제대로 대응하지 못했고, 결국 GPT에 의존하게 됐다.</p>
<p>게다가 미션은 Vite를 빌더 도구로 사용하고 있지만, 언젠가 Parcel을 써보고 싶어질 수도 있다는 생각이 들었다. 그럴 때 자유롭게 도구를 바꾸고, 원하는 구성 요소를 넣다 뺄 수 있으려면 환경에 대한 이해가 필요하다고 느꼈다.</p>
<p>그래서 이번엔 누군가 미리 깔아준 환경이 아니라, 바닥부터 직접 하나씩 쌓아올리는 방식을 택했다. create-react-app이나 create-vite처럼 손쉽게 초기 설정을 끝내주는 도구들도 일부러 사용하지 않았다.</p>
<p><a href="https://github.com/kaori-killer/playground">세팅한 레포</a></p>
<h1 id="세팅-목록">세팅 목록</h1>
<ul>
<li>기본 작업</li>
<li>TypeScript</li>
<li>ESLint</li>
<li>Prettier</li>
<li>React</li>
<li>Vite</li>
</ul>
<h1 id="기본-작업">기본 작업</h1>
<h2 id="1️⃣-프로젝트-폴더-생성-및-이동">1️⃣ 프로젝트 폴더 생성 및 이동</h2>
<p>노드 버전 확인 → 노드 설치 → 프로젝트 폴더 생성 및 이동</p>
<pre><code class="language-jsx">node -v

fnm install --lis
fnm list

mkdir my-app
cd my-app
code .</code></pre>
<h2 id="2️⃣-npm-패키지-생성">2️⃣ npm 패키지 생성</h2>
<pre><code class="language-jsx">npm init -y</code></pre>
<h2 id="3️⃣-ignore-세팅">3️⃣ ignore 세팅</h2>
<p><a href="https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore">.gitignore 파일</a>을 생성한다.</p>
<pre><code class="language-jsx">touch .gitignore</code></pre>
<h1 id="typescript">TypeScript</h1>
<h2 id="1️⃣-typescript-설치">1️⃣ TypeScript 설치</h2>
<pre><code class="language-jsx">npm i -D typescript</code></pre>
<h2 id="2️⃣-설정-파일-생성">2️⃣ 설정 파일 생성</h2>
<pre><code class="language-jsx">npx tsc --init</code></pre>
<h2 id="3️⃣-tsconfigjson-파일-수정">3️⃣ tsconfig.json 파일 수정</h2>
<p>JSX 파일을 쓰겠다고 선언하는 부분이다. 하지만 이 부분이 없어도 정상적으로 동작한다.</p>
<pre><code class="language-Jsx">&quot;jsx&quot;: &quot;react-jsx&quot;</code></pre>
<h1 id="eslint">ESLint</h1>
<h2 id="1️⃣-eslint-설치">1️⃣ ESLint 설치</h2>
<pre><code class="language-jsx">npm i -D eslint</code></pre>
<h2 id="2️⃣-eslint-설정">2️⃣ Eslint 설정</h2>
<pre><code class="language-jsx">npx eslint --init</code></pre>
<p>상황에 맞게 질문에 대답한다.</p>
<pre><code class="language-Jsx">설치할까요? y
Eslint 어떤 거에 사용할까요? 모두
자바스크립트 모듈 어떤거 사용할까요? JavaScript (import/export)
프레임워크는 어떤 거 쓸까요? react
타입스크립트 사용하나요? y
Browser, Node? Browser
스타일 가이드를 따르세요? 아니면 매번 물어볼까요? 전자
스탠다드를 따르세요? xo를 따르세요? xo
설정파일 어떻게 잡으시겠어요? js
추가 설치해도 되나요? y
패키지 매니저 뭐 쓸까요? npm (기본)</code></pre>
<h2 id="3️⃣-eslintconfigmjs-파일-수정">3️⃣ eslint.config.mjs 파일 수정</h2>
<ul>
<li><strong>react version 명시</strong> <ul>
<li>eslint-plugin-react는 React의 버전에 따라 룰의 동작을 다르게 적용한다.<ul>
<li><strong>react/react-in-jsx-scope&#39; 규칙을 &#39;off&#39;로 설정</strong></li>
</ul>
</li>
<li>JSX 자동 런타임을 쓸 때 React를 import하지 않아도 되므로, 해당 규칙을 꺼준다.</li>
<li>React 17부터 JSX 자동 런타임이 도입되어 import React from &quot;react&quot; 없이 JSX를 사용할 수 있게 되었다.</li>
<li>하지만 eslint-plugin-react는 기본적으로 JSX가 있으면 React를 import했는지 검사한다.<ul>
<li>예전에는 JSX 파일에서 React를 직접 import하지 않아도 되게 하려면 <code>plugin:react/jsx-runtime</code>을 extends에 추가하면 됐지만 현재는 안됨.</li>
<li><strong>ignore 설정</strong> </li>
<li>ESLint는 기본적으로 모든 디렉토리를 검사하려고 한다. 그래서 검사에서 제외할 파일을 지정한다.</li>
<li>이전에는 .eslintignore 파일로 설정했는데 버전이 바뀌면서 방식이 바뀜.</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">export default defineConfig([
  { files: [&quot;**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}&quot;], plugins: { js }, extends: [&quot;js/recommended&quot;], },
  { files: [&quot;**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}&quot;], languageOptions: { globals: globals.browser}, 
  },
  tseslint.configs.recommended,
  pluginReact.configs.flat.recommended,
  {
    // 🚨 react version 명시
    settings: {
      react: { 
        version: &quot;detect&quot;,
      },
    },
  },
  {
    // 🚨 &#39;react/react-in-jsx-scope&#39; 규칙을 &#39;off&#39;로 설정
    rules: {
      &quot;react/react-in-jsx-scope&quot;: &quot;off&quot;,
      &quot;react/jsx-uses-react&quot;: &quot;off&quot;,
      indent: [&#39;error&#39;, 2],
    },
    // 🚨 ignore 설정
    ignores: [&#39;**/node_modules/**&#39;, &#39;**/dist/**&#39;, &#39;**/build/**&#39;],
  },
]);</code></pre>
<h2 id="4️⃣-eslint-확장자-설치">4️⃣ ESLint 확장자 설치</h2>
<ol>
<li>VS Code 확장에서 VS Code ESLint extension를 설치한다.</li>
<li>vscode 폴더를 만들고 안에 settings.json 파일을 만든다.</li>
</ol>
<pre><code class="language-jsx">mkdir .vscode
touch .vscode/settings.json</code></pre>
<p>settings.json 파일에는 Lint가 잡을 것을 설정한다. 
예를 들어, <code>80열에서 줄 긋기</code>, <code>save하면 끝에 있는 줄 삭제</code>를 추가할 수 있다.</p>
<pre><code class="language-jsx">{
  &quot;editor.rulers&quot;: [80],
  &quot;editor.codeActionsOnSave&quot;: {
    &quot;source.fixAll.eslint&quot;: &quot;always&quot; // S/TS 파일을 저장할 때마다 ESLint를 실행하고 문제점을 고치게 설정
  },
  &quot;trailing-spaces.trimOnSave&quot;: true
}</code></pre>
<h1 id="react">React</h1>
<h2 id="1️⃣-react-설치">1️⃣ React 설치</h2>
<pre><code class="language-jsx">npm i react react-dom</code></pre>
<h2 id="2️⃣-type-react-설치">2️⃣ Type React 설치</h2>
<pre><code class="language-jsx">npm i -D @types/react @types/react-dom</code></pre>
<h2 id="3️⃣-react-기본-파일-생성">3️⃣ React 기본 파일 생성</h2>
<pre><code class="language-jsx">// /index.html
&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
    &lt;head&gt;
        &lt;meta charset=&quot;UTF-8&quot;&gt;
        &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
        &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
        &lt;title&gt;Document&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
        &lt;script type=&quot;module&quot; src=&quot;main.tsx&quot;&gt;&lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre>
<pre><code class="language-tsx">// /main.tsx
import React from &quot;react&quot;;
import ReactDOM from &quot;react-dom/client&quot;;
import App from &quot;./src/App&quot;;

ReactDOM.createRoot(document.getElementById(&quot;root&quot;)!).render(
  &lt;React.StrictMode&gt;
    &lt;App /&gt;
  &lt;/React.StrictMode&gt;
);</code></pre>
<pre><code class="language-tsx">// src/App.tsx
export default function App() {
  return &lt;&gt;Hello&lt;/&gt;;
}</code></pre>
<h1 id="packagejson">Package.json</h1>
<pre><code class="language-jsx">  &quot;scripts&quot;: {
    &quot;dev&quot;: &quot;vite&quot;,
    &quot;build&quot;: &quot;tsc &amp;&amp; vite build&quot;,
    &quot;check&quot;: &quot;tsc --noEmit&quot;,
    &quot;lint&quot;: &quot;eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0&quot;,
    &quot;test&quot;: &quot;jest&quot;,
    &quot;coverage&quot;: &quot;jest --coverage --coverage-reporters html&quot;,
    &quot;watch:test&quot;: &quot;jest --watchAll&quot;,
    &quot;preview&quot;: &quot;vite preview&quot;,
    &quot;storybook&quot;: &quot;storybook dev -p 6006&quot;,
    &quot;build-storybook&quot;: &quot;storybook build&quot;
  },</code></pre>
<h1 id="🍵-마무리">🍵 마무리</h1>
<p>세팅을 하면서 Velog 같은 레거시 문서를 참고하지 않고, 공식 문서를 참고했다. 이유는 버전이 바뀌면서 설치되는 파일도 달라지고 설정 방식도 바뀌었기 때문에 레거시 문서를 믿을 수 없었다.</p>
<p>예를 들어, ESLint도 예전에는 JSX 파일에서 React를 직접 import하지 않아도 되게 하려면 <code>plugin:react/jsx-runtime</code>을 extends에 추가하면 됐었다. 그런데 현재 사용하는 Flat config에서는 <code>plugin:</code> 접두사가 더 이상 지원되지 않기 때문에 이 설정을 쓸 수 없었다. 그래서 <code>react/react-in-jsx-scope</code> 규칙을 <code>off</code>로 설정해서 React import를 강제하지 않도록 바꿨다.</p>
<p>공식 문서를 따라가며 하나하나 설정해보니, 생각보다 어렵지 않았다. 앞으로는 create-react-app 같은 도구의 도움을 받더라도 필요한 설정은 직접 커스텀할 수 있겠다는 자신감이 생겼다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴포넌트 패턴에 대해 알아보자! (2)]]></title>
            <link>https://velog.io/@kaori-killer/%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%ED%8C%A8%ED%84%B4%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90-2</link>
            <guid>https://velog.io/@kaori-killer/%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%ED%8C%A8%ED%84%B4%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90-2</guid>
            <pubDate>Sun, 29 Jun 2025 08:10:01 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/kaori-killer/post/e2bb4919-c2d7-43d3-b32e-1e7c2a1ea29c/image.png" alt="">
<img src="https://velog.velcdn.com/images/kaori-killer/post/73e9919d-f71e-4e1e-9219-f19b0ff3922b/image.png" alt=""></p>
<h1 id="1️⃣-제어비제어-컴포넌트">1️⃣ 제어/비제어 컴포넌트</h1>
<p><code>Controlled and Uncontrolled Component Patterns</code></p>
<p>지금까지 프로그래밍을 하면서 거의 제어 컴포넌트만 썼다. 이유는 모두 실시간으로 검증이 필요해서였다. 비제어 컴포넌트를 선택했다면 어떤 이벤트 트리거로 검증을 했을까? onSubmit?</p>
<p>제어와 비제어 컴포넌트를 비교해보면, 제어 컴포넌트는 값과 변경 이벤트를 컴포넌트에 연결하고, 변경 이벤트를 줄 때마다 계속해서 Re-Rendering이 발생한다. 비제어 컴포넌트는 DOM 자체에 값을 저장하고 필요할 때 객체(ref)를 통해 직접 값을 가져온다.</p>
<p>실시간 필드 유효성 검사도 필요없고, 특정 입력 형식(input type)도 강제하지 않고 등등 사용자에게 완전 열여주고 싶으면 비제어 컴포넌트를 쓸 것 같다. 생각해보면, 네이버에서 로그인 할 때도 비제어 컴포넌트 같다.</p>
<pre><code class="language-tsx">// 제어 컴포넌트
const ControlledInput = () =&gt; {
  const [value, setValue] = useState(&#39;&#39;);

  const handleChange = ({ target: { value } }) =&gt; {
    setValue(value);
  };

  return &lt;input value={value} onChange={handleChange} /&gt;;
};</code></pre>
<pre><code class="language-tsx">// 비제어 컴포넌트
const UnControlledInput = () =&gt; {
  const inputRef = useRef(null);

  const handleSubmit = (event) =&gt; {
    event.preventDefault();
    // inputRef.current.value
  };

  return (
    &lt;form onSubmit={handleSubmit}&gt;
      &lt;input ref={inputRef} /&gt;
      &lt;button type=&quot;submit&quot;&gt;Submit&lt;/button&gt;
    &lt;/form&gt;
  );
};</code></pre>
<h2 id="💡-useref">💡 useRef</h2>
<ul>
<li>useRef()로 만든 값은 사실 그냥 { current: ... } 형태의 일반적인 자바스크립트 객체다.<ul>
<li>이 객체는 자바스크립트에서 Heap 메모리라는 곳에 저장된다.</li>
<li>Heap은 전역 변수, 참조 변수를 할당하거나 GC로 참조되지 않는 메모리를 해제한다.</li>
</ul>
</li>
<li>애플리케이션의 종료 또는 GC 되기 전까지 참조 시 같은 메모리 값을 가진다.<ul>
<li>같은 메모리 주소를 갖으니 일치 연산자의 결과가 항상 참이므로 변경사항이 없다.</li>
<li>즉, 렌더링 시점에서 항상 동일한 객체가 제공되고, 변경사항이 감지되지 않아 리렌더링 되지 않는다.</li>
</ul>
</li>
</ul>
<h1 id="2️⃣-합성-컴포넌트">2️⃣ 합성 컴포넌트</h1>
<p><code>Compound Components Pattern</code> </p>
<p><img src="https://velog.velcdn.com/images/kaori-killer/post/55d41ecf-c9f9-40d9-aa83-7de171699b73/image.png" alt=""></p>
<p>모달 모듈을 만드는 미션에서 사용했던 패턴이다. 만약 이 패턴을 안 쓰고 구현했다면, 왼쪽처럼 단일 컴포넌트에 여러가지 조건문을 통해 스타일링을 했을 것이다. 실제로 그렇게 구현한 크루는 prop 개수가 엄청 많았다. 요구사항이 복잡해질 수록 컴포넌트가 그 요구사항을 모두 내포하기 위해서 비대해질 수 밖에 없다. 합성 컴포넌트 패턴을 사용하면 이를 조립하는 형태로 해결할 수 있다.</p>
<ul>
<li>Q. 언제 합성 컴포넌트 패턴을 쓰면 좋을까?</li>
</ul>
<p>위에서 말했지만, 여러 조건에 따라 조립해서 사용하는 형태의 컴포넌트를 구현할 때 유용할 것 같다.</p>
<ul>
<li>Q. 어떻게 해야 합성 컴포넌트 패턴이 안티 패턴처럼 느껴지지 않을까?</li>
</ul>
<p>합성 컴포넌트는 컴포넌트를 트리 형태로 조립해 쓸 수 있기 때문에, props drilling 문제를 줄여줄 수 있는 장점이 있다. 하지만 적절한 추상화 없이 컴포넌트만 조립해두면 오히려 구조가 복잡해서 흐름이 불명확해질 수도 있을 것 같다.</p>
<pre><code class="language-jsx">// on 일 때만 메시지 나옴
const ToggleOn = ({ children }) =&gt; {
    const { on } = useContext(ToggleContext);
    return on ? children : null;
};

// off 일 때만 메시지 나옴
const ToggleOff = ({ children }) =&gt; {
    const { on } = useContext(ToggleContext);
    return on ? null : children;
};

const ToggleButton = (props) =&gt; {
    const { toggle } = useContext(ToggleContext);
    return &lt;button onClick={toggle} {...props} /&gt;;
};

const App = () =&gt; {
    return (
        &lt;Toggle&gt;
            &lt;ToggleOn&gt;ON&lt;/ToggleOn&gt;
            &lt;ToggleOff&gt;OFF&lt;/ToggleOff&gt;
            &lt;ToggleButton&gt;Toggle&lt;/ToggleButton&gt;
        &lt;/Toggle&gt;
    );
};</code></pre>
<p>Context API를 이용하니까 데이터에 신경 쓰지 않고, 사용자 입장에선 정말 컴포넌트만 조립해서 사용할 수 있게 되는구나 싶었다. 또 데이터를 보여줄지 말지를 어느 컴포넌트가 결정해야 할지 고민했는데, 처음엔 부모가 책임지는 게 나을까 싶다가도, 결국엔 각 컴포넌트가 스스로 책임지는 게 더 깔끔하구나 하는 생각이 들었다.</p>
<h2 id="💡-어떤-구조를-선택할-것인가">💡 어떤 구조를 선택할 것인가?</h2>
<p>위에서는 ToggleOn, ToggleOff처럼 각 컴포넌트를 따로 작성했지만, Toggle 컴포넌트에 속성처럼 붙여서 Toggle.On, Toggle.Off 형태로도 작성할 수 있다. 몇몇 라이브러리에서 이런 방식을 쓰는데, 토스에서는 Toggle.On처럼 구성하는 걸 사용하더라. 장단점이 있다. 후자 방식의 장점은, Toggle 하나만 import하면 Toggle에 연결된 모든 하위 컴포넌트를 사용할 수 있다. 또, Toggle.까지만 입력해도 어떤 컴포넌트들이 있는지 자동완성으로 확인할 수 있어 사용하기 편하다. 하지만 단점도 있다. 이 방식은 모든 하위 컴포넌트를 함께 가져온다고 가정하기 때문에, 실제로 사용하지 않아도 전부 번들에 포함된다. 그래서 Tree Shaking이나 Code Splitting이 되지 않아 번들 사이즈가 커질 수 있다.</p>
<p>결국엔 개발자 경험과 성능 사이에서 균형을 잡아야 하는 선택이고, 프로젝트 상황에 따라 어떤 방식이 더 적절한지 판단해야 한다고 생각한다.</p>
<pre><code class="language-jsx">Toggle.On = ({ children }) =&gt; {
    const { on } = useContext(ToggleContext);
    return on ? children : null;
};

Toggle.Off = ({ children }) =&gt; {
    const { on } = useContext(ToggleContext);
    return on ? null : children;
};

Toggle.Button = (props) =&gt; {
    const toggle = useContext(ToggleContext);
    return &lt;button onClick={toggle} {...props} /&gt;;
};

const App = () =&gt; {
    return (
        &lt;Toggle&gt;
            &lt;Toggle.On&gt;OK&lt;/Toggle.On&gt;
            &lt;Toggle.Off&gt;OFF&lt;/Toggle.Off&gt;
            &lt;Toggle.Button&gt;토글하기&lt;/Toggle.Button&gt;
        &lt;/Toggle&gt;
    );
};</code></pre>
<h1 id="🍵-마무리">🍵 마무리</h1>
<p>여러 컴포넌트 패턴에 대해 알아봤는데 생각보다 재밌었다. GitHub에서 이 패턴들을 잘 적용한 코드를 많이 찾아보고 싶다. 그리고 직접 적용해보면서 언제 어떤 패턴을 쓰는 게 자연스러운지 감을 잡아가고 싶다.</p>
<p>다만, 모든 상황에 패턴이 정답인 건 아니니까 괜히 억지로 적용하지는 말자. <del>패턴병</del></p>
<blockquote>
<p>홀맨이 해준 조언: 과거 코드를 답습하기보단 이제는 계속 실무에서 실질적인 문제를 다뤄가면서 설계에서 발생하는 트레이드 오프 지점을 몸으로 느끼고 결정하는게 훨씬 중요할 거에요. 레퍼런스는 어차피 이상적인 특정 상황을 가정하고 만들어 둔거라 실무에서 너무 그 &#39;모양&#39;에만 집중해서 따라하려 하는건 오히려 독이 되기 쉽습니다. 개념적으로 방향을 이해했다면 이제는 실무 도메인에서 여러 가지 상황을 겪는게 제일 중요합니다. 절대 생각하는 방향으로 이상적으로 구조를 만들 수 없는 이유를 느끼는게 더 중요할 거 같네요. 그게 오히려 실력이 더 늘고 추후에 설계 관련된 책들을 다시 봤을 때 와닿는 느낌이 전혀 다를 겁니다. </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴포넌트 패턴에 대해 알아보자! (1)]]></title>
            <link>https://velog.io/@kaori-killer/%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%ED%8C%A8%ED%84%B4%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90-1</link>
            <guid>https://velog.io/@kaori-killer/%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%ED%8C%A8%ED%84%B4%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90-1</guid>
            <pubDate>Sat, 28 Jun 2025 07:58:44 GMT</pubDate>
            <description><![CDATA[<p>관심사의 분리, 의존성 줄이기, 책임과 역할의 명확화, 공통 로직 분리 등... 훅이 너무 강력해서 대부분의 문제를 해결해주는 듯 싶다. 하지만 어떤 코드를 훅으로 분리해야 할지 인지하는 것이 아직 어렵다. 그래서 컴포넌트 패턴을 학습해보기로 했다. 사람들이 어떤 코드를 &#39;문제&#39;라고 느끼는지, 그리고 그것을 꼭 훅이 아니더라도 어떻게 해결하는지 살펴보았다.</p>
<h1 id="1️⃣-비지니스-로직과-ui-분리하기">1️⃣ 비지니스 로직과 UI 분리하기</h1>
<p><code>Container and Presentational Components Pattern</code></p>
<p>한 컴포넌트 안에 비즈니스 로직과 UI가 섞여 있을 때, 관심사를 분리하기 위해 사용하는 패턴이다. 비즈니스 로직은 Container 컴포넌트로 분리하고, UI는 오직 UI만 책임지도록 구성한다.</p>
<p>좋았던 점은, UI를 그리는 컴포넌트를 쉽게 교체할 수 있다는 것이다. 재사용성이 자연스럽게 올라갔다. 실제로 나도 한 컴포넌트에 모든 걸 몰아넣는 경우가 종종 있었는데, 이 패턴을 접하고 나니 컴포넌트도 결국 함수이니 하나의 책임만 가져야겠다는 생각이 들었다. 결과적으로 가독성도 더 좋아졌다.</p>
<pre><code class="language-tsx">import { useQuery } from &#39;react-query&#39;;
import { PaymentCardList } from &#39;./payment-card-list&#39;;

// 비즈니스 로직의 주체(컨테이너)는 비지니스 로직을 전부 갖는다.
export const PaymentCardListContainer = () =&gt; {
  const { loading, data: cardList } = useQuery({
    // ...
  });

  // UI를 그리는 주체(프리젠테이션)는 결과 값만 받아서 UI만 그려준다. 비지니스 로직과 상관X
  return &lt;PaymentCardList loading={loading} cardList={cardList} /&gt;;
};</code></pre>
<h1 id="2️⃣-동일-로직을-여러-컴포넌트에-제공하기-1">2️⃣ 동일 로직을 여러 컴포넌트에 제공하기 (1)</h1>
<p><code>Higher-Order Component Pattern</code></p>
<p>중복 로직을 하나로 모으기 위한 패턴이다. HOC라는 이름을 처음 봤을 때는 &#39;함수를 반환하는 함수&#39;로 어떻게 문제를 해결하지? 라는 생각이 들었다. 그런데 실제로는 하나의 컴포넌트에 중복 로직을 몰아넣고, 렌더링할 컴포넌트를 인자로 받아 해결한다. &#39;역시 자바스크립트니까 이런 것도 가능하구나&#39; 싶었다. 로그인 인증처럼 여러 페이지에서 반복되는 로직이 있을 때 유용해 보였다.</p>
<pre><code class="language-tsx">// 🚨 변경 전: 페이지마다 중복 로직 발생
const MyPage = () =&gt; {
  const { isLoggedIn } = useAuth();

  if (!isLoggedIn) {
    return &lt;div&gt;로그인이 필요한 페이지입니다.&lt;/div&gt;;
  }

  return &lt;p&gt;마이 페이지&lt;/p&gt;;
};

const OrderPage = () =&gt; {
  const { isLoggedIn } = useAuth();

  if (!isLoggedIn) {
    return &lt;div&gt;로그인이 필요한 페이지입니다.&lt;/div&gt;;
  }

  return &lt;p&gt;주문 페이지&lt;/p&gt;;
};</code></pre>
<pre><code class="language-tsx">// ✅ 변경 후: 동일 로직을 여러 컴포넌트에 제공

// 첫 번째 인자: 함수 컴포넌트
// 두 번째 인자: 로그인될 때 튕길지 아닐지
const withAuth = (Component, avoidNotLoggedUser = false) =&gt; {
    return (props) =&gt; {
        const { isLoggedIn } = useAuth();
        if (avoidNotLoggedUser &amp;&amp; isLoggedIn) {
            return &lt;div&gt; 로그인 이 필요한 페이지입니다.&lt;/div&gt;;
        }
        return &lt;Component {...props} /&gt;;
    };
};

const HomePage = withAuth(() =&gt; &lt;div&gt;메인 페이지&lt;/div&gt;);
const MyPage = withAuth(() =&gt; &lt;p&gt;마이 페이지&lt;/p&gt;, true);</code></pre>
<h2 id="⚠️-주의할-점-props-충돌-가능성">⚠️ 주의할 점: props 충돌 가능성</h2>
<p>HOC 패턴은 렌더링 대상 컴포넌트에 props를 주입하는 방식이라, 원래 컴포넌트가 기대하는 props와 충돌할 수 있는 위험이 있다.</p>
<pre><code class="language-tsx">const withUser = (Component) =&gt; {
  return (props) =&gt; {
    const user = useUser();
    return &lt;Component user={user} {...props} /&gt;; // props.user와 충돌 가능!
  };
};</code></pre>
<p>user라는 prop이 이미 외부에서 전달되었다면, withUser가 덮어쓰게 된다. 이처럼 의도치 않은 prop 덮어쓰기가 발생할 수 있다는 점은 HOC의 한계로 지적된다.</p>
<h1 id="3️⃣-동일-로직을-여러-컴포넌트에-제공하기-2">3️⃣ 동일 로직을 여러 컴포넌트에 제공하기 (2)</h1>
<p><code>Render Props Pattern</code></p>
<p>데이터 공유 구조는 HOC와 유사하지만,</p>
<ul>
<li>props 충돌을 방지할 수 있고</li>
<li>생명주기를 더 유연하게 활용할 수 있다는 장점이 있다.</li>
</ul>
<pre><code class="language-tsx">// 🚨 변경 전: 상태가 props로 흩어짐
const Display = ({ count }) =&gt; {
    return &lt;h1&gt;{count}&lt;/h1&gt;;
};

const Counter = ({ count, setCount }) =&gt; {
    return &lt;input type=&quot;number&quot; value={count} onChange={(e) =&gt; setCount(e.target.value)} /&gt;;
};

const App = () =&gt; {
    const [count, setCount] = useState(0);

    return (
        &lt;div&gt;
            &lt;Display count={count} /&gt;
            &lt;Counter count={count} setCount={setCount} /&gt;
        &lt;/div&gt;
    );
};</code></pre>
<p>처음 이 코드를 봤을 땐 setCount 같은 상태 변경자가 props로 내려오는 구조가 불편하게 느껴졌다. 상태 변경 책임이 여러 컴포넌트에 흩어져 있어 예측이 어렵고, 추적하기도 힘들겠다는 생각을 했다. 나라면 훅으로 빼거나 핸들러 함수를 만들어 처리했을 것 같다.</p>
<p>그런데 Render Props 패턴에서는 상태를 컴포넌트 안에 고립시켜 관리할 수 있다. HOC처럼 함수를 인자로 받지만, UI 렌더링 책임을 render 함수로 넘기는 구조이다.</p>
<ul>
<li><strong>HOC</strong>: 로직을 감싸고, UI는 넘겨받은 컴포넌트가 담당</li>
<li><strong>Render Props</strong>: 로직을 내부에 갖고, UI는 render 함수 prop으로 위임</li>
</ul>
<pre><code class="language-tsx">// ✅ 변경 후: 상태 고립 + 렌더 위임
type CounterProps = {
    render: (count: number, change: () =&gt; void) =&gt; JSX.Element;
};

const Counter = ({ render }: CounterProps) =&gt; {
    const [count, setCount] = useState(0);

    const increment = () =&gt; setCount((prev) =&gt; prev + 1);

    return render(count, increment);
};</code></pre>
<pre><code class="language-tsx">import { Counter } from &#39;./counter&#39;;

const App = () =&gt; {
    return (
        &lt;div&gt;
            &lt;Counter render={(count, increment) =&gt; (
                &lt;div&gt;
                    &lt;h1&gt;{count}&lt;/h1&gt;
                    &lt;button onClick={increment}&gt;+1&lt;/button&gt;
                &lt;/div&gt;
            )} /&gt;
        &lt;/div&gt;
    );
};</code></pre>
<h3 id="✅-render-props의-장점-1-props-충돌-방지">✅ Render Props의 장점 1. props 충돌 방지</h3>
<p>렌더링을 위한 함수를 명시적으로 넘기므로, 내부에서 어떤 props가 들어올지 예측 가능하다.</p>
<h3 id="✅-render-props의-장점-2-생명주기와-상태-로직의-유연한-활용">✅ Render Props의 장점 2. 생명주기와 상태 로직의 유연한 활용</h3>
<p>Render Props는 일반적인 컴포넌트이기 때문에, useEffect, useState, useRef 등의 훅을 자유롭게 사용할 수 있다. UI는 외부에서 제어할 수 있지만, 상태와 생명주기는 내부에 고립시킬 수 있다는 점에서 HOC보다 구조적으로 유연하다.</p>
<p>물론 HOC 내부에서도 useState나 useEffect를 쓸 수 있다. 하지만, 컴포넌트 계층이 감춰지면서 디버깅이 어렵고 여러 HOC가 중첩되면 컴포넌트 추적이 복잡해지며, props 충돌 위험이 항상 존재한다. 예를 들어... withTheme(withUser(...))</p>
<p><del>예전에 클래스 컴포넌트가 사라진 이유 중에 HOC와 같은 문제가 있다는 걸 봤다.</del></p>
<h1 id="4️⃣-render-props-pattern-확장">4️⃣ Render Props Pattern 확장</h1>
<p><code>Prop Collections (or Combination) Pattern</code> </p>
<p>Render Props Pattern의 확장이다. 넘겨줄 props를 객체 형태로 묶어서 전달한다. 상위에서 내려오는 props를 예쁘게 가공해서 하위 컴포넌트에 전달할 때 사용한다고 한다.</p>
<p>페이먼츠 미션처럼 input이 많고, 비슷한 props를 반복적으로 내려줘야 할 때 이 패턴이 유용할 수 있겠다는 생각이 들었다.</p>
<pre><code class="language-tsx">const Button = ({ label, color, size, ...rest }) =&gt; {
    return (
        &lt;p style={{ color, fontSize: size }} {...rest}&gt;
            {label}
        &lt;/p&gt;
    );
};

const App = () =&gt; {
    const buttonProps = {
        label: &#39;클릭&#39;,
        color: &#39;red&#39;,
        size: &#39;20px&#39;,
        onClick: () =&gt; console.log(&quot;버튼이 클릭 되었습니다!&quot;),
    };
    return &lt;Button {...buttonProps} /&gt;;
};</code></pre>
<p>위 예시만으로는 이 패턴이 잘 와닿지 않아서 조금 더 찾아보았고, 아래처럼 사용하면 좋을 것 같다.</p>
<pre><code class="language-tsx">const getCounterProps = (count, increment) =&gt; ({
  onClick: increment,
  children: count,
});

// 필요한 props를 묶어서 전달 → 재사용성 ↑
&lt;Counter&gt;
  {({ count, increment }) =&gt; {
    const props = getCounterProps(count, increment);
    return &lt;button {...props} /&gt;;
  }}
&lt;/Counter&gt;</code></pre>
<h1 id="5️⃣-추상화로-컴포넌트에-역할-추가하기">5️⃣ 추상화로 컴포넌트에 역할 추가하기</h1>
<p><code>Conditional Rendering Pattern</code> 또는 <code>Disabled Prop Pattern</code></p>
<p>조건부 렌더링을 통해 컴포넌트의 책임을 나누는 패턴이다. 처음에는 단순한 조건 하나인데 굳이 추상화할 필요가 있을까? 싶었지만, 조건이 많아질수록 하나의 컴포넌트에 관심사가 너무 많아질 수 있다는 점을 알게 되었다. App 컴포넌트에 disabled 관련 조건이 존재하는 이유가 한눈에 드러나지 않는다면, 오히려 추상화해서 렌더링 책임을 명확히 분리하는 편이 가독성에 더 좋을 수 있다.</p>
<p>물론 지나치게 추상화되면 오히려 구조가 더 복잡해 보일 수 있다.</p>
<pre><code class="language-jsx">// 🚨 변경 전
const App = ({ disabled }) =&gt; {
  return (
    &lt;div&gt;
      {!disabled &amp;&amp; (
        &lt;div&gt;
          &lt;h1&gt;특정 상황에서만 그려집니다.&lt;/h1&gt;
        &lt;/div&gt;
      )}
    &lt;/div&gt;
  );
};</code></pre>
<pre><code class="language-jsx">// ✅ 변경 후
const ConditionalComponent = ({ disabled }) =&gt; {
  return (
      {!disabled &amp;&amp; (
        &lt;div&gt;
          &lt;h1&gt;특정 상황에서만 그려집니다.&lt;/h1&gt;
        &lt;/div&gt;
      )}
  );
};

const App = ({ disabled }) =&gt; {
  return (
    &lt;div&gt;
      &lt;ConditionalComponent disabled={disabled} /&gt;
    &lt;/div&gt;
  );
};</code></pre>
<h1 id="🍵-마무리">🍵 마무리</h1>
<p>실제로 내 코드에 적용해본 건 아니라서 엄청 와닿는다고는 할 수 없지만, &#39;이런 상황에 써보면 좋겠는데?&#39; 하는 생각이 들었다. 꼭 훅만이 관심사 분리의 정답은 아니라는 것도 알게 되었다.</p>
<p>제어/비제어 컴포넌트, 합성 컴포넌트 등 아직 이 글에서 다루지 못한 컴포넌트 패턴이 몇 가지 더 있다. 이 패턴들은 실제로 사용해본 경험이 있는 만큼, 좀 더 자세히 써보고 싶어서 다음 글에서 이어서 정리해보려 한다.</p>
]]></description>
        </item>
    </channel>
</rss>