<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://parkgang.github.io/blog</id>
    <title>parkgang.log Blog</title>
    <updated>2025-06-07T09:50:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://parkgang.github.io/blog"/>
    <subtitle>parkgang.log Blog</subtitle>
    <icon>https://parkgang.github.io/img/logo.png</icon>
    <entry>
        <title type="html"><![CDATA[『술술 읽히는 쉬운 영문법』 후기]]></title>
        <id>/2025/06/07/smooth-english-grammar-book-review</id>
        <link href="https://parkgang.github.io/blog/2025/06/07/smooth-english-grammar-book-review"/>
        <updated>2025-06-07T09:50:00.000Z</updated>
        <summary type="html"><![CDATA[그동안 듀오링고로 영어를 공부하고 있는데 문장 조립만 하기에 문법을 알기 어려워 한번 문법 정리가 필요하다고 느끼고 있었다.]]></summary>
        <content type="html"><![CDATA[<p>그동안 듀오링고로 영어를 공부하고 있는데 문장 조립만 하기에 문법을 알기 어려워 한번 문법 정리가 필요하다고 느끼고 있었다.</p><p>어디 좋은 방법 없나 찾아보다 <a href="https://youtu.be/K9rcR6SVzwA?si=jGgCUMXce8k9wqaJ&amp;t=108" target="_blank" rel="noopener noreferrer">유튜브 영상</a>의 추천을 통해 읽게 되었다. 이 책은 너무 깊지도, 너무 길지도 않게 18일 동안 볼 수 있어 호감이었다. 몰랐는데 읽는 당시 알라딘에서 영문법 베스트셀러 4위로 인기 있는 책이었다.</p><p>영어를 많이 접하고 output을 하는 것도 중요하지만 기본적인 문법 이해는 중요하다고 생각한다. 프로그래밍 언어와 비유를 해도 적어도 기초 문법은 알아야 이해를 하고 궁금한 것을 찾아서 발전이 있을 것이다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAPAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAABQf/xAAiEAACAgICAAcAAAAAAAAAAAABAgMEABEFIRIjMUFScZH/xAAVAQEBAAAAAAAAAAAAAAAAAAAEBv/EAB4RAAIBAwUAAAAAAAAAAAAAAAECAwAEEQUxQaHR/9oADAMBAAIRAxEAPwCqXLnOpdkWHj4ShchCbajafLWvrr1xmtPbetE09QRylAXQSBvC2uxv3wWtZtclVp2r1c12EpmjSObQYAkKW1vfW+sTHIyaHlL+5HTaPcsqhUGedvaUL+CNjlj3X//Z&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="500" height="740"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/thumbnail.da02f82.500.jpg" srcset="/assets/ideal-img/thumbnail.da02f82.500.jpg 500w" width="500" height="740"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="진행한-공부-방법">진행한 공부 방법<a class="hash-link" href="#진행한-공부-방법" title="제목으로 바로 가기">​</a></h2><ul><li>회사 점심시간 (20분)</li><li>휴일 (1시간)</li><li>챕터 3개 지나면 복습</li><li>한번 다 읽은 뒤 전체 복습</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="공부-기간">공부 기간<a class="hash-link" href="#공부-기간" title="제목으로 바로 가기">​</a></h2><ul><li>공부+복습 (2025년 4월 17일 → 2025년 5월 28일 <code>약 1개월 2주 소요</code> )</li><li>공부 후 전체 복습 (2025년 5월 28일 → 2025년 6월 7일 <code>약 1.5주 소요</code> )</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="후기">후기<a class="hash-link" href="#후기" title="제목으로 바로 가기">​</a></h2><ul><li>듀오링고를 보면서 햇갈렸던 문법이 정리되는 느낌<ul><li>보면 볼수록 의문이 들었던 것이 해결되는 느낌</li><li>어떻게 보면 그만큼 듀오링고를 통하며 문법의 의미를 찾아내려고 노력했다는 것…</li></ul></li><li>교과서 처럼 문법이 딱 정리된 것이 아니라 동화책 읽듯이 서술된 것이 특징<ul><li>그래서 내가 따로 정리할 때 이해하지 못하면 못함</li></ul></li><li>문법을 공부해보니 왜 문법에 매몰되면 어려운지 느낀다…<ul><li>대화, 문장이라는 것이 특정 구조를 안다고 해서 만들어지는 것이 아니고 문장을 만들 수 있는 사고가 중요하다.</li><li>문법책을 보면서 어떻게 조립하는 방법을 어깨넘어 배울 뿐 실제 활용은 많은 문장을 경험해야할 것</li></ul></li><li>영문법을 배우는 이유는 미묘한 늬앙스를 표현하기 위함이라고 한다. 그래서 사실 문법이 틀려도 원하는 바는 말할 수 있다. 하지만 그 미묘한 늬앙스를 위해서 이렇게 열심히 배우는 것이다.<ul><li>예시로 아래의 차이를 보면 젊다 와 젊어보인다는 그 미묘한 늬앙스를 위해 문법을 맞추는 것을 볼 수 있다.<ul><li>She look young for her age. (그녀는 나이에 비해 어려 보여.)</li><li>She is young for her age. (그녀는 나이에 비해 젊습니다.)</li></ul></li></ul></li><li>단어를 외워도 단수, 복수, 시제 등에 따라 시도때도 없이 변하니 외우려고 하면 고통스러움. 단어를 암기하려고 드는 것 보다는 어느정도만 이해하고 문장을 많이 봐야한다고 생각.</li><li>많이 반복해서 봐야한다고 느끼었고 그렇게 하려고 했는데 생각보다 오래 걸릴 것 같다. 빠르게 학습하는 능력을 기르는 것도 중요하다고 느끼는 중</li><li>한국어로는 뉘앙스, 디테일을 알아차리기 쉬운데 영어로 그걸 하려고 하니 어렵다. 공손하든, 짧든 다 똑같아 보이는 영어 문장인데 해석하면 뜻이 바뀌어 버리니… 이런 건 영어 문법 공부를 안 하고는 알기 어려운 디테일들이 있었다.</li><li>긴 영어 문장 해석 능력이 중요할 듯<ul><li>DAY 16의 관계대명사 하면서 크게 느낌</li><li>Coke is not the beverage which I want to drink. (콜라는 내가 마시고 싶은 음료가 아니야.)를 전체로 해석하면 이해가 되지만, 이걸 내가 이해하는 문장 단위로 해석을 해버리면 Coke is not the beverage (콜라는 음료가 아닙니다.)로 시작부터 해석이 이상해진다.</li></ul></li><li>자꾸 한국어랑 변환해서 1:1 매칭하려고 하지 말아라. 영어 자체의 문장을 이해해 봐라. 뉘앙스가 서로 다르다.</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>성인이 되어 영어 공부를 시작한 뒤로 문법 공부를 제대로 해본 적은 없기에 새로운 문법이 나올 때마다 신기하고 탐구할 수 있어서 재미있었습니다. 한편으로는 문법에 따라 문장의 뜻, 뉘앙스가 너무 많아 문법을 마스터하겠다!라는 마음으로 접근하면 끝이 안 나겠다는 생각이 들었습니다. 우선 문장을 많이 보고 만드는 법부터 익힌 뒤 문법을 적용하며 뉘앙스를 보강하는 것이 좋겠다는 생각이 들었습니다.</p><p>간단하게 문법을 알려줘서 좋았지만, 암기할 수 있도록 교과서처럼 정리되지 않은 것은 아쉬웠습니다.</p><p>이제 겨우 아주 간단한 문법을 보았는데 영어의 막막함이 느껴졌습니다. 그래도 이제 문장을 보며 적어도 배웠던 문법은 상기시키면서 해석할 수 있을 것 같습니다.</p><p>문법 공부는 멈추고 많은 영어 문장을 보고, 해석하고, 만드는데 집중해야겠습니다.</p>]]></content>
        <category label="책 후기" term="책 후기"/>
        <category label="영어" term="영어"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[parkgang.log(2024)]]></title>
        <id>/2025/04/19/2024-retrospective</id>
        <link href="https://parkgang.github.io/blog/2025/04/19/2024-retrospective"/>
        <updated>2025-04-19T06:40:00.000Z</updated>
        <summary type="html"><![CDATA[너무 늦은 2024 회고, 나에 대한 고민과 변화가 많아 힘들었던 한 해]]></summary>
        <content type="html"><![CDATA[<p>너무 늦은 2024 회고, 나에 대한 고민과 변화가 많아 힘들었던 한 해</p><p>회고를 작성해야지 하면서 쌓인 일에 밀리고 밀려 이제서야 작성하게 되었다.</p><p>2024년은 나에게 힘들었던 한 해이었다. 끊임없는 고민들, 나는 과연 잘하고 있는 걸까? 뭐가 맞는 걸까? 괴로운 시간이 더 많았던 것 같다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="모바일-로보틱스-강사">모바일 로보틱스 강사<a class="hash-link" href="#모바일-로보틱스-강사" title="제목으로 바로 가기">​</a></h2><p>나는 <code>모바일 로보틱스</code> 기능반 출신이다.</p><p>고등학교 졸업 후 강사를 꽤 다니가 끊기었는데 오랜만에 연락이 와서 강사로 나가게 되었다.
무려 <code>3년 5개월</code> 만이다. 감회가 새롭더라.</p><p>비록, 나는 원하던 <code>전국기능경기대회</code> 매달을 따지 못했지만, 강사를 통해 그런 학생들을 만나고, 나를 보고 싶었다고 하는 학생들을 보니 뿌듯했다.</p><p>또 언제 이런 기회가 올지 모르지만 오랜만에 느끼는 뿌듯한 감정이었다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="생산성-향상-타임-블로킹">생산성 향상: 타임 블로킹<a class="hash-link" href="#생산성-향상-타임-블로킹" title="제목으로 바로 가기">​</a></h2><p>할 일 관리를 하면서 불편한 것들이 많았다. 어떻게 해야 단순하고, 원하는 집중력을 가져갈 수 있을까 고민이 있었는데 마침 <code>Notion Calendar</code> 도 출시되고 <code>타임 블로킹</code> 을 알게 되어 시도해 보았다.</p><p>많은 시행착오가 있었는데 너무 많은 것을 기록하려는 것이 문제이었다. 개선을 통해 많이 나아지긴 했는데 집중력 향상보다는 하루하루 기록의 의미가 커진 것 같다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="크레이프크레페-먹기">크레이프(크레페) 먹기<a class="hash-link" href="#크레이프크레페-먹기" title="제목으로 바로 가기">​</a></h2><p>나는 뭔가 하나씩 꽂히는 것들이 생기더라. 유튜브를 보다 우연히 크레이프를 보게 되었고, 너무나 맛있을 것이라는 생각이 사라지지 않았다.</p><p>그리하여 크레페 도장 깨기를 시작하게 되었는데... <code>메가커피</code> , <code>동대문 할아버지 크레페</code> , <code>덕수궁 카페 돌담콩</code> 를 먹게 되었다.</p><p>나름 재밌는 에피소드이었다. 2년 전에는 붕어빵 도장 깨기 이었는데 앞으로 또 어떤 것이 나를 사로잡을지 기대된다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="본가-이사">본가 이사<a class="hash-link" href="#본가-이사" title="제목으로 바로 가기">​</a></h2><p>본가 이사를 하게 되었다. 마침 인테리어 공사가 어느 정도 되어있는 집이기도 하고, 비록 내 집은 아니지만 새롭게 구매한 것들이 많은 만큼 이쁜 집을 만들고 싶었다.</p><p>사실 이번 기회에 인테리어 경험도 생기면 앞으로 내 집이 생겼을 때 도움이 될 것 같았다.</p><p>하지만 그 과정은 만만치 않았다ㅠㅠ 그래도 끝나고 보니 이쁜 화이트 모던 하우스가 되었고 주변 분들도 이쁘다고 해주셔서 뿌듯했다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="부서-이동">부서 이동<a class="hash-link" href="#부서-이동" title="제목으로 바로 가기">​</a></h2><p>부서 이동을 하게 되어 Stack이 또 바뀌게 되었다. 이동할까 고민이 많았지만 나에게 더 도움이 될 수 있을 것 같아서 이동하게 되었다.</p><p>적응 후 잘 지내고 있는데 Stack이 계속 바뀌니 연속성이 없어서 아쉽다. 하지만 경험해 보고 싶은 새로운 Stack이기에 즐겁게 공부하고 있다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="건강에-대한-감사함">건강에 대한 감사함<a class="hash-link" href="#건강에-대한-감사함" title="제목으로 바로 가기">​</a></h2><p>나는 평소에 건강에 대한 감사함을 잘 느끼지 못했다. 일례로 영양제 먹는 것을 이해 못 했다. 하지만 지금은 뼈저리게 느끼고 있다.</p><p>들으면 웃길지 모르지만 현대 의학이 많이 발전하여 병원 가면 한 번에 다 낫는 줄 알았다. 내가 그 정도로 아파서 병원에 갈 일이 없어서 그런 것도 있다.</p><p>운동을 열심히 해서 그런지, 나이가 들어서 그런지 몰라도 조금씩 아픈 부분이 생기는데 죽을 병은 아니고 삶의 질이 떨어지는 것들이었다. 근데 이런 것들이 쉽게 고쳐지지 않더라.</p><p>그래서 건강에 대한 감사함을 느끼게 되었고, 앞으로는 더 소중히 여겨야겠다는 생각이 들었다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="공부">공부<a class="hash-link" href="#공부" title="제목으로 바로 가기">​</a></h2><p>뭔가 한 것은 있는데 각 잡고 공부한 것은 없다. Stack이 바뀌기에 넓고 새롭게 공부해야 하는 것들이 많아서 그런 것도 있지만, 나의 공부 방법도 문제가 있는 것 같다.</p><p>이전에는 뭐든 배우는 것이 도움이 된다고 생각했는데 임팩트 있는 부분에서 쓰임이 발생하지 않으니 마음이 복잡하다. 도움이 안 된 것들은 아니다. 기술의 이해, 근본으로 들어갈수록 도움이 될 것이라고 믿었는데 잘 발생하지 않는다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="운동">운동<a class="hash-link" href="#운동" title="제목으로 바로 가기">​</a></h2><p>새로운 도전이 많았다.</p><ul><li>자전거 100km<ul><li>자전거를 꽤 꾸준히 탔는데 장거리로 타본 적이 없었다. 그래서 100km 도전해 보았고 성공적으로 완주했다.</li></ul></li><li>수영<ul><li>나는 물이랑 친하지 않다. 그런데 물이랑 친하지 않다는 것은 많은 핸디캡이 있더라. 더 늦기 전에 배우고 싶었다. 하지만 <code>어깨 충돌 증후군</code> 으로 중간에 중단하게 되었다...</li></ul></li><li>러닝 시작(하프)<ul><li>러닝화를 구매하고 러닝을 시작했다. 이후 <code>하프</code> 까지 완주하며 승승장구 중이다.</li></ul></li><li>설악산 등산<ul><li>서울의 유명한 산은 가보아서 대한민국에서 유명한 산을 가보고 싶었다. 마침 근방에 <code>설악산</code> 이 있어서 가보았다. 만만하게 보았는데 생각보다 좀 많이 힘들었다.</li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="여행">여행<a class="hash-link" href="#여행" title="제목으로 바로 가기">​</a></h2><p>새로운 사람과, 새로운 지역을 가볼 수 있었다.</p><ul><li>가족 충북 여행</li><li>전주 단기 여행</li><li>베트남 다낭 여행<ul><li>해외로는 일본만 가보아서 <code>동남아</code> 여행이 궁금했다. 새로운 건물, 문화, 음식을 만나 견문이 넓어졌다.</li></ul></li><li>가평-양양 여행</li><li>군산 여행</li><li>홍콩-마카오 여행<ul><li>해외여행지 중 <code>홍콩</code> 은 관심이 없던 곳이었다. 회사에서 여행을 보내준다고 하여 가보게 되었는데 역사적으로 재밌는 곳 이더라.</li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="종합">종합<a class="hash-link" href="#종합" title="제목으로 바로 가기">​</a></h2><p>후퇴한 것은 아니지만 앞으로 나아간 것도 아닌 1년이었다.<br>
<!-- -->나에 대한 고민과 환경에 대한 고민이 많았던 한 해였다.<br>
<!-- -->아직은 뭐가 맞는지 몰라서 고민은 계속될 것 같다.<br>
<!-- -->더욱더 단순하게 살아가려고 노력하려 한다.</p>]]></content>
        <category label="개인 회고" term="개인 회고"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[내가 AI를 코딩에 활용하는 방법]]></title>
        <id>/2025/03/27/ai-for-coding</id>
        <link href="https://parkgang.github.io/blog/2025/03/27/ai-for-coding"/>
        <updated>2025-03-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[바야흐로... 2023년 쯤 부터 ChatGPT의 붐으로 AI가 엄청난 속도로 발전했다. 이제는 AI 도구를 잘 사용하는 것도 실력이라고 생각한다. 내가 어떻게 잘 사용하는지 소개해보자.]]></summary>
        <content type="html"><![CDATA[<p>바야흐로... 2023년 쯤 부터 ChatGPT의 붐으로 AI가 엄청난 속도로 발전했다. 이제는 AI 도구를 잘 사용하는 것도 실력이라고 생각한다. 내가 어떻게 잘 사용하는지 소개해보자.</p><p>ChatGPT 등장만 해도 프로젝트를 시작할 때 궁금한 것에 대한 정보를 빨리 찾을 수 있어서 좋았고, GitHub Copilot으로 코드 생성해주는 AI도 나오고… 더 나아가 Cursor라는 제품으로 AI전용 IDE까지 나오는 실정이다.</p><p>더 이상 코드를 만들어내는 개발자는 경쟁력이 적은 상황이고 AI를 <strong>적극사용</strong> 해서 생산력을 높이는 것이 주요 과제로 보인다.</p><p>특히, Cursor가 나오면서 네임드 개발자 X의 글을 보면 Cursor을 주로 사용하고 이거 때문에 코드의 가치가 낮아졌다고 할 정도이다.</p><p>아직 AI가 개발자를 대체할 수준은 아니라고 생각되며 대체될 정도의 코드만 만들 줄 아는 개발자는 어차피 대체될 운명이었을 것이다. AI 기능도 잘 사용해야 생산성이 올라간다. 발전 속도가 워낙 빨라 언젠가 레거시 글이 될 수 있지만 내가 사용한 사례를 정리해본다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="ai를-이용한-코딩-사용-형태">AI를 이용한 코딩 사용 형태<a class="hash-link" href="#ai를-이용한-코딩-사용-형태" title="제목으로 바로 가기">​</a></h2><ul><li>빠른 분석<ul><li>예제코드로 흐름 분석</li><li>코드 베이스 주석으로 코드 값 같은 것 빠르게 확인</li></ul></li><li>빠른 컨셉, 샘플 코드 검증<ul><li>대량의 코드 수정은 정확도가 떨어지지만 레고 블럭처럼 조금 만한 것을 확인하기에는 유용</li></ul></li><li>새롭게 만들기<ul><li>빠르게 분석해서 러프하게 요구사항을 뜯어내고</li><li>그걸 기반으로 코드를 만들어서 버그를 잡는 것이 코드 퀄리티도 생산성도 나쁘지 않음</li></ul></li><li>리펙토링</li><li>만든거 테스트 코드 짜달라고 하기</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="ai-코딩-기능">AI 코딩 기능<a class="hash-link" href="#ai-코딩-기능" title="제목으로 바로 가기">​</a></h2><ul><li>General Chat (e.g. ChatGPT)<ul><li>일반적이고 넓은 질문</li><li>주로 코드 동작 근본 자체에 대한 질문을 물어봄</li><li>UI 자체가 Chat에만 집중되어 편함<ul><li>IDE에 들어간 Chat은 급하게 사용하기는 좋지만 채팅이 길어지는 작업에 대해서는 거부감이 느껴지는데 ChatGPT 같은 것은 그런 거부감이 없음</li></ul></li><li>e.g.<ul><li>Spring MVC가 뭐야?</li><li>Spring MVC에서 권장되는 코드 패턴은?</li></ul></li></ul></li><li>Embedded Chat (e.g. GitHub Copilot Chat)<ul><li>코드베이스 기준으로 넓게 물어볼 때</li><li>주로 코드 어딘가에 있을거 같은데… 그런 것을 물어볼 때 사용</li><li>코딩 지식을 물어보지는 않음. 그런 것은 ChatGPT 이용</li><li>e.g.<ul><li>전표 항목을 불러오는 JS 코드 부분은?</li><li>해당 파일에서 거래처 항목을 저정하고 있는 변수 이름은?</li></ul></li></ul></li><li>Embedded Composer (e.g. Cursor Composer, WindSurf Write)<ul><li>코드베이스 기준으로 여러 파일은 한번에 수정 할 때 사용</li><li>Chat이 질문, 분석이라면 해당 기능은 수정이 가해지는 작업입니다.</li><li>e.g.<ul><li>A라고 작성된 주석을 B로 바꿔줘 (코드베이스에서 정의된 부분 모두 찾아서 바꿔줌)</li><li>Next.js 예제 프로젝트 만들어줘 (여러 파일을 생성하여 프로젝트 구성)</li></ul></li></ul></li><li>Embedded Copilot (e.g. GitHub Copilot)<ul><li>코드 생성 및 수정에 주로 활용</li><li>inline으로 호출되어 빠르게 사용할 수 있고 상호작용이 매우 직관적임</li><li>e.g.<ul><li>(드래그 후) 코드 리팩토링 해줘</li><li>(주석 작성 후, 자동완성으로 나오는 코드 사용)</li></ul></li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="유용한-조건테크닉">유용한 조건(테크닉)<a class="hash-link" href="#유용한-조건테크닉" title="제목으로 바로 가기">​</a></h2><ul><li>프로젝트<ul><li>문서화가 잘 되어 있고 널리 사용되는 언어, 라이브러리, 프레임워크일수록 참고 자료가 풍부해 더 효과적으로 활용할 수 있다.</li><li>프로젝트 코드베이스에 대해 질문할 때도 코드 의존 관계와 코드 품질이 좋을수록 더 정확한 답변을 얻을 수 있다.</li></ul></li><li>제너럴 채팅 (ChatGPT)<ul><li>간단한 개념 질문은 <code>4o</code>를, 심화 질문은 <code>o1</code>을 활용한다.<ul><li>개념이나 사용법 같은 기본적인 내용은 <code>4o</code>, 트러블슈팅과 같은 심화 내용은 <code>o1</code></li></ul></li><li>대화 중 주제를 바꿔야 한다면 새로운 채팅방을 시작하라<ul><li><code>o1</code>으로 SQL 실행 계획 분석에 대해 깊이 물어보고 있다가 Hash Join, Nested Join 같은 다른 주제가 궁금해지면 새 채팅방을 열어라</li><li>기존 채팅방에서 주제를 바꾸면 문맥이 혼란스러워지고 나중에 내용을 추적하기 어려워진다.</li></ul></li><li>특정 기능의 사용법을 물어볼 때는 가능하면 버전을 명시하거나, 어렵다면 help 명령어나 공식 문서를 참조하는 방식으로 접근하라<ul><li>Oracle DB 트러블슈팅 중 경험</li><li>ChatGPT는 <code>show incident</code> 명령에 대해 최신 버전의 방법을 알려주는 것 같았지만, 내가 사용 중인 버전에서는 작동하지 않았다.</li><li><code>help show incident</code> 명령으로 단계적으로 해결책을 찾을 수 있었다.<ul><li><code>show incident 248096 -mode detail</code> → <code>show incident -mode detail -p "incident_id=248096"</code></li></ul></li></ul></li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="use-case">Use Case<a class="hash-link" href="#use-case" title="제목으로 바로 가기">​</a></h2><ul><li><p>하나의 Project에서 여러 예제 프로젝트를 만들어서 PoC 진행</p><ul><li>실 제품은 의존된 것도 많고, 커스텀 된 설정도 많기에 문제 해결 과정이 복잡해지는 경우가 있습니다.</li><li>vanilla 환경에서 올바른 구현을 보고 검증해서 제품에 끼워넣는 것이 명확한데 AI Editor은 빠른 프로젝트를 만들 수 있게 하여 PoC에 유용했습니다.</li><li>e.g. 특정 라이브러리로 된 기본 프로젝트를 만들어줘 -&gt; 여기서 원하는 설명만 명확하게 바꿔서 어떻게 동작하는지 이해 후 적용</li></ul></li><li><p>코드에 주석 작성해달라고 함</p><ul><li>일일이 Break Point 찍으면서 값을 보더라도 enum 같은 값인 경우 뭔지 사전을 찾아서 까봐야 이해가 됩니다. DB 데이터도 마찬가지이구요.</li><li>이런 경우 궁금한 코드를 드래그해서 주석을 작성해달라고 하면 사람이 이해하는 단위로 적어주니 코드 읽기가 훨씬 수월해집니다.</li><li>e.g.<ul><li>예시1<div class="language-java codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-java codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 거래처 일련번호</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">accSlipIC</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">put</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"sqCnct"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sqCnct</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 거래처 구분</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">accSlipIC</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">put</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"tpCnct"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> orgCnct</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"TP_CNCT"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 거래처명</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">accSlipIC</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">put</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"ttlCnct"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> orgCnct</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"TTL_CNCT"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li>아예 코드를 작성해 AI 쿼리하는 형식, <code>switch</code> 이기 때문에 <code>case</code> 에 대한 값을 AI가 자동으로 추론하도록 만듬<div class="language-js codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-js codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 전표 유형에 따른 분기 처리</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// J: 대체전표</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// I: 입금전표</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword control-flow" style="color:#00009f">switch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">accSlipH</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">TP_SLIP_I</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"J"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">break</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"I"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">break</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li></ul></li></ul></li><li><p>디버깅하기 쉽게 코드 변경</p><ul><li>코드 라인 수 줄인다고 아래와 같이 합치고 합치면 보기 힘든데 이런 구조를 손쉽게 바꿔달라고 할 수 있을 것<div class="language-java codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">before</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-java codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">accSlipIC</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">put</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"ttlAcccd"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">String</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">acccdC</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"TTL_ACCCD"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="language-java codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">after</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-java codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> ttlAcccd </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">String</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">acccdC</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"TTL_ACCCD"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">accSlipIC</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">put</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"ttlAcccd"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ttlAcccd</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li></ul></li><li><p>어느정도 추측되는 부분까지는 찾았는데 자세히 어떤 흐름인지 모르겠을때</p><ul><li>AI가 모든 것을 만능으로 해주지 않습니다.</li><li>그치만 아래와 같이 명확히 원인이 되는 부분이 찾았다면 이 흐름을 기점으로 역으로 찾는 질문은 큰 도움을 받을 수 있습니다.</li><li>e.g. 간접전표에서 전표 데이터가 아래의 하위 element으로 추가된다는 것은 확실히 알겠는데 어디서 어떻게 추가되는지 감이 오지 않았고 해당 코드를 기준으로 어디서 추가되는지 질문하여 찾아냄.<div class="language-html codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-html codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">tbody</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">slipListBody</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">tbody</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAXUlEQVQImT2HORLDIBDAeIYXNmQxznCZuOH/X1PGLlJoJLkcX2wivFXZLSEixBgxsz/3O8sf0tEoe+UqX46UCRpQVUIIeO8fuzEG55zMc3J3bY3eO63159dalFr5AcS2KkNyCER8AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="618" height="215"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.7448add.618.png" srcset="/assets/ideal-img/1.7448add.618.png 618w" width="618" height="215"></noscript></div></div></li></ul></li><li><p>특정 코드 부분에 대해서 샘플 코드</p><ul><li>변수 값, 함수를 안다고해도 어떤 의도로 코드가 작성되었는지 모르면 햇갈립니다.</li><li>그런 경우 특정 부분에 샘플 코드를 만들어 달라고하면 input, output이 명시적으로 보여서 이해하기 편합니다.</li><li>e.g. 코드 흐름 중 <code>key.substr(3)</code> 값만 보았을 때는 뭔소리인가 싶지만 실제 제품에서 사용되는 예제 값으로 input, output을 보니 손쉽게 이해됨</li></ul></li><li><p>코드 분석 후 input, output만 지정해서 새롭게 작성하기</p><ul><li>Unit Test 코드 작성처럼 input, output만 지정하고 구현체는 AI에 맡기기</li><li>새로운 시각에서 코드를 바라볼 수 있음</li></ul></li><li><p>코드 의존성 분석</p><ul><li>표 <code>footer</code> 에 표시되던 것을 <code>body</code> 으로 이동했는데 <code>footer</code> <code>DOM</code> 을 뜯어서 계산하는 로직이 있어서 Side Effect가 발생했습니다.</li><li>모든 부분을 다 찾을 수는 없겠지만 참조로 추적이 안되는 부분까지 찾아 도움 받을 수 있음</li></ul></li><li><p>코드 구조 바꾸기 좋음</p><ul><li><p>아래의 코드를</p><div class="language-html codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-html codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">tr</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  필드 이름</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">tr</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">tr</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  필드 내용</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">tr</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>아래의 조건에 맞게 바꾸고 싶을 때 많은 귀찮은 작업이 발생하는데</p><div class="language-html codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-html codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">tr</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">label</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">필드 이름</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">label</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  필드 내용</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">tr</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>아래와 같이 프롬프트 해서 손쉽게 바꿀 수 있음</p><div class="codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-text codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">&lt;tr&gt; 안에 구조가 &lt;td&gt;라벨명&gt;&lt;/td&gt;&lt;td&gt;요소&lt;/td&gt; 으로 이뤄져있어</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">이걸 아래의 형식으로 리펙토링 해줘</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;td&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;label&gt;라벨명&lt;/label&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">요소</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;/td&gt;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAALCAYAAABGbhwYAAAACXBIWXMAABYlAAAWJQFJUiTwAAABD0lEQVQYlWXN22rCQBSF4byDjjN7jskkJsZpNUqV0sNFCxbp+7/PX0xLofTiY8FmsXYlWrNUGjEO0YJaKhaLxUwphTFmVokIzgXGoVA2hTZ3pJSw1v6WjNZUWmtWWtP1Pamu0cb8s7otemcJYun3r0QXiMYQRfBa/1GlGNhGYRrWPIwj+yaxq8Oc04/7nKhiCuRkWXeZaTex395RhoGcHHUQmmBpgtwWPa0TNjlT+g1DSvQp0TpLtubX/DpGS91mxrJjOxbarsU5gw8W72VWBWcpS8XBRY6hYa+FO6Upi+UfVRDhXRTX5PlsO67R8+ENF1Fc7Oo7RVHlJnI4t5xeCk9vJ87PheNjz3TO83368QWkJZd33xokUwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1674" height="1938"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.59db28b.1674.png" srcset="/assets/ideal-img/2.59db28b.1674.png 1674w" width="1674" height="1938"></noscript></div></div></li></ul></li></ul>]]></content>
        <category label="AI" term="AI"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[CTE 쿼리에 컬럼 별칭으로 지정된 슈퍼 키 성능 개선]]></title>
        <id>/2025/02/20/cte-superkey-column-alias-optimization</id>
        <link href="https://parkgang.github.io/blog/2025/02/20/cte-superkey-column-alias-optimization"/>
        <updated>2025-02-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[회계 시스템에서 입력된 전표 데이터를 특정 기준에 따라 집계해 보고서로 보여주는 기능에 속도 이슈가 발생했다.]]></summary>
        <content type="html"><![CDATA[<p>회계 시스템에서 입력된 전표 데이터를 특정 기준에 따라 집계해 보고서로 보여주는 기능에 속도 이슈가 발생했다.</p><p>이 문제를 어떻게 해결했는지 경험을 공유하고자 한다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="상황">상황<a class="hash-link" href="#상황" title="제목으로 바로 가기">​</a></h2><p>회계 시스템에서 입력된 전표 데이터를 바탕으로 재무제표 등 여러 보고서를 조회할 수 있는데, 조회 속도가 느릴 때는 10초, 심할 때는 2분 이상 걸리는 문제가 있었다.</p><p>긴 쿼리 중 문제가 되었던 예시 쿼리는 다음과 같다.</p><div class="language-sql codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-sql codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">WITH</span><span class="token plain"> ACC_SOURCE </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token comment" style="color:#999988;font-style:italic">-- 슈퍼 키</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        ID_ORG </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">' '</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> SQ_ACCSES </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">' '</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> ID_ACCCD </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> SUPER_KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        A</span><span class="token punctuation" style="color:#393A34">.</span><span class="token operator" style="color:#393A34">*</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> ACC_SOURCE_OG A</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     X1_TBL </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> ACC_SOURCE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">WHERE</span><span class="token plain"> SUPER_KEY </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'A00'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     X2_TBL </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> X1_TBL</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">WHERE</span><span class="token plain"> SUPER_KEY </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'A10'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">-- 원래 이후 더 많은 쿼리 존재...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> AMT_DR</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> AMT_CR</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> X2_TBL</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>ACC_SOURCE_OG에서 여러 컬럼을 조합해 SUPER_KEY를 만들고, 이를 기준으로 X1_TBL, X2_TBL을 생성해 최종적으로 AMT_DR, AMT_CR을 조회하는 쿼리다.</p><p>위에서 아래로 내려가면서 ACC_SOURCE_OG → ACC_SOURCE → X1_TBL → X2_TBL 순으로 CTE(Common Table Expression) 쿼리가 체이닝되어 있는 것을 볼 수 있다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="느린-원인-분석">느린 원인 분석<a class="hash-link" href="#느린-원인-분석" title="제목으로 바로 가기">​</a></h2><p>하위 쿼리에서는 Source 테이블에 계속 접근해야 하는데 KEY가 컬럼 별칭으로 존재하고 있었다.</p><p>CTE이기 때문에 하위 쿼리에서 조회할 때마다 재평가되어 큰 오버헤드가 발생했고, SUPER_KEY 역시 컬럼 별칭이라 Index가 없어 Full-Scan이 반복되고 있었다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="개선-방법">개선 방법<a class="hash-link" href="#개선-방법" title="제목으로 바로 가기">​</a></h2><p><strong>CTE로 정의된 테이블은 서브 쿼리처럼 사용할 때마다 계산된다.</strong> 그래서 CTE를 물리 테이블로 분리하는 것만으로도 어느 정도 속도가 개선된다. (10분이 넘어도 조회가 안 되던 쿼리가 4초 만에 조회될 정도로 빨라졌다. 하지만 Index를 사용하면 500ms 이하로 훨씬 더 빨라진다.)</p><p>KEY를 컬럼 별칭으로 사용하다 보니 Full-Scan이 발생했는데, Index를 지정하면 훨씬 더 빨라질 것이다.</p><p>이에 따라 Source 물리 테이블에 SUPER_KEY를 물리 컬럼으로 추가하고, Index를 생성하여 해결했다.</p><div class="language-sql codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-sql codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">WITH</span><span class="token plain"> X1_TBL </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> ACC_SOURCE_OG</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">WHERE</span><span class="token plain"> SUPER_KEY </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'A00'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     X2_TBL </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> X1_TBL</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">WHERE</span><span class="token plain"> SUPER_KEY </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'A10'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">-- 원래 이후 더 많은 쿼리 존재...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> AMT_DR</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> AMT_CR</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> X2_TBL</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="속도-개선-결과">속도 개선 결과<a class="hash-link" href="#속도-개선-결과" title="제목으로 바로 가기">​</a></h2><p>정말 느린 경우는 14분(840초) 걸리던 것이 1.2초로 줄어 <strong>700배</strong> 빨라졌다.</p><p>적어도 전체적으로 3배 이상은 빨라졌다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>결론을 보면 당연히 KEY 값은 물리 컬럼으로 PK를 지정해서 Index가 되어야 하는 것 아니야? 싶지만, 그렇지 않은 쿼리도 존재했고... 복잡한 시스템 사이에서 이걸 찾기란 쉽지 않았다. 이런 결론에 도달하는 데 많은 시간이 걸렸다.</p><p>느린 부분을 찾고 해결을 하며 속도 개선이 눈에 보이니 재미있었다.</p><p>이번 경험을 통하여 CTE 쿼리의 성능 이슈를 알게 되었고, Index를 활용해 성능 개선을 직접 경험할 수 있었다.</p><p>누군가 이 개선 사례를 바탕으로 도움이 되길 바라며 글을 마친다.</p>]]></content>
        <category label="RDB" term="RDB"/>
        <category label="성능 개선" term="성능 개선"/>
        <category label="CTE" term="CTE"/>
        <category label="Index" term="Index"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Vim 삭제 명령이 OS 클립보드를 덮어쓰는 문제 해결하기]]></title>
        <id>/2025/02/10/vim-prevent-delete-overwrite-clipboard</id>
        <link href="https://parkgang.github.io/blog/2025/02/10/vim-prevent-delete-overwrite-clipboard"/>
        <updated>2025-02-10T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Vim에서 삭제는 잘라내기로 동작한다. OS에서 복사한 값을 붙여넣기 전 텍스트를 정리하며 제거된 값이 OS에서 복사한 값을 지워버린다. 왜냐면 잘라내기로 동작하기 때문이다. 이 불편함을 어떻게 해결할 수 있을까?]]></summary>
        <content type="html"><![CDATA[<p>Vim에서 삭제는 잘라내기로 동작한다. OS에서 복사한 값을 붙여넣기 전 텍스트를 정리하며 제거된 값이 OS에서 복사한 값을 지워버린다. 왜냐면 잘라내기로 동작하기 때문이다. 이 불편함을 어떻게 해결할 수 있을까?</p><p>Vim 클립보드 내용을 검색하면 다들 OS 클립보드 연동하는 방법만 있지 Vim에서 삭제 시 OS 클립보드 값을 지키는 내용은 없더라. 나만 불편한 것인가...?</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="문제-원인">문제 원인<a class="hash-link" href="#문제-원인" title="제목으로 바로 가기">​</a></h2><p>보통 <code>set clipboard=unnamedplus</code> 설정하여 OS ↔ Vim 클립보드를 <strong>동기화</strong> 한다.</p><p>OS ↔ Vim 클립보드와 <strong>연결</strong>만 하면 복사, 붙여넣기 시 매번 <code>"+y</code> , <code>"+p</code> 같이 <code>+</code> , <code>*</code> 레지스터 지정해야 해서 불편하다.</p><p><code>set clipboard=unnamedplus</code> 문제는 OS에서 복사한 값이 <code>dd</code> , <code>x</code> 같이 <code>Vim</code> 제거 동작 시 사라진다는 것이다.</p><p>상황에 따라 다르지만 보통 복사 후 붙여넣기 전까지 입력도 하고 삭제도 하며 텍스트 가공이 이루어지는데 삭제를 해버리면 OS 클립보드 값이 사라져서 곤란하다.</p><p><code>Vim</code> 의 <code>0</code> 번 레지스터는 삭제가 이루어져도 유지되는 것과 다르다. 이유는 OS ↔ Vim 클립보드를 동기화했기 때문에 삭제하는 것도 OS에 동기화하면서 발생한다.</p><p>문제 해결을 위해선 OS의 값을 붙여넣기 전까지 블랙홀 레지스터로 OS에서 복사한 값을 잘 유지해야 한다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="해결-방법">해결 방법<a class="hash-link" href="#해결-방법" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="손쉽게-블랙홀-레지스터로-보내기">손쉽게 블랙홀 레지스터로 보내기<a class="hash-link" href="#손쉽게-블랙홀-레지스터로-보내기" title="제목으로 바로 가기">​</a></h3><p>블랙홀 레지스터 단축키 입력이 생각보다 많이 불편하다.</p><p>그래서 처음에는 블랙홀 레지스터를 손쉽게 보내도록 키 맵핑을 했다.</p><div class="codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-text codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">" 블랙홀 레지스터 쉽게 호출할 수 있도록 맵핑</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nnoremap &lt;Space&gt;x "_x</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vnoremap &lt;Space&gt;x "_x</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nnoremap &lt;Space&gt;X "_X</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vnoremap &lt;Space&gt;X "_X</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nnoremap &lt;Space&gt;s "_s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vnoremap &lt;Space&gt;s "_s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nnoremap &lt;Space&gt;S "_S</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vnoremap &lt;Space&gt;S "_S</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nnoremap &lt;Space&gt;c "_c</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vnoremap &lt;Space&gt;c "_c</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nnoremap &lt;Space&gt;C "_C</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vnoremap &lt;Space&gt;C "_C</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nnoremap &lt;Space&gt;d "_d</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vnoremap &lt;Space&gt;d "_d</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nnoremap &lt;Space&gt;D "_D</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vnoremap &lt;Space&gt;D "_D</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nnoremap &lt;Space&gt;dd "_dd</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vnoremap &lt;Space&gt;dd "_dd</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="삭제와-잘라내기-동작-구분">삭제와 잘라내기 동작 구분<a class="hash-link" href="#삭제와-잘라내기-동작-구분" title="제목으로 바로 가기">​</a></h3><p>하지만 이것 또한 귀찮다.</p><p>근본적으로 Vim의 레지스터 동작이 보편적으로 사용되는 OS과 달라서 생긴 문제라고 생각이 들었다. <code>삭제 != 잘라내기</code> 인데 Vim은 <code>삭제 = 잘라내기</code> 동작을 하니 말이다. 그래서 잘라내기만 동작하도록 명시적으로 키 맵핑을 지정해서 해결했다.</p><div class="language-lua codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-lua codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">--</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">--</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 명시적인 호출 시에만 삭제 레지스터로 이동</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 삭제를 하더라도 OS 클립보드 값을 보호하기 위함</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 기본적으로 삭제는 블랙홀 레지스터 사용</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"d"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_d'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"d"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_d'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"D"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_D'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"D"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_D'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"dd"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_dd'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"dd"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_dd'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"x"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_x'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"x"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_x'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"X"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_X'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"X"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_X'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"s"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_s'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"s"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_s'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"S"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_S'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"S"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_S'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"c"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_c'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"c"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_c'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"C"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_C'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"C"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'"_C'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- Space + 삭제 입력 시 원래 동작(클립보드 사용) 유지: 잘라내기 동작</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;d"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"d"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;d"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"d"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;D"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"D"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;D"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"D"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;dd"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"dd"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;dd"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"dd"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;x"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"x"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;x"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"x"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;X"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"X"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;X"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"X"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;s"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"s"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;s"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"s"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;S"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"S"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;S"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"S"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;c"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"c"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;c"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"c"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;C"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"C"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keymap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"v"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;Space&gt;C"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"C"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>이렇게 하면 아래의 장/단점을 가지게 된다.</p><ul><li>장점<ul><li>보편적인 OS에서 사용하는 것과 동일하게 클립보드 관리 가능</li><li>복사한 것은 다른 복사가 오거나 잘라내기 전까지 유지된다.</li><li>삭제는 삭제다.</li><li>잘라내기 시 해당 값으로 복사 값이 변경된다.</li></ul></li><li>단점<ul><li>Vim에서 삭제 시 <code>"1</code> ~ <code>"9</code> 으로 저장되는 삭제 히스토리를 사용하지 못함</li><li>개인적으로는 <code>"1</code> ~ <code>"9</code> 을 사용한 적이 거의 없다. 코딩하면서 삭제는 워낙에 빈번하게 일어나기에 기억도 안난다. 설령 제거한 적을 저장하고 싶다면 명시적으로 레지스터를 할당에서 저장하는 것이 관리도 기억도 잘 될 것이라고 생각한다.</li><li>macOS의 경우 RayCast를 통해서 클립보드 히스토리 관리가 가능하기 때문에 기능 보안도 가능하다.</li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>삭제와 잘라내기 동작을 구분하도록 키 맵핑 후 한동안 삭제 후 왜 삭제 레지스터에 값이 들어가지 않았지? 하고 해매고 있다.</p><p>적응이 되면 편해질 것 같지만 꼭 이렇게 Vim 동작을 바꿔야하나 생각이 든다.</p>]]></content>
        <category label="Vim" term="Vim"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[LazyVim 입문 가이드: 설치부터 디버깅까지 한 번에 끝내기]]></title>
        <id>/2025/02/09/lazyvim-guide</id>
        <link href="https://parkgang.github.io/blog/2025/02/09/lazyvim-guide"/>
        <updated>2025-02-09T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[나도 나만의 멋진 Vim Config를 가질 줄 알았다. 하지만 그 과정은 고통스럽고 시간 낭비가 심하다.]]></summary>
        <content type="html"><![CDATA[<p>나도 나만의 멋진 Vim Config를 가질 줄 알았다. 하지만 그 과정은 고통스럽고 시간 낭비가 심하다.</p><p>인텔리제이, VSCode 등 시간이 지나며 발전하고 유려한 AI 도구 지원이 되는 것을 보여 왜 이렇게 해야하나 싶다.</p><p>그럼에도 Vim은 텍스트 수정, 터미널을 돌아다니며 conf 수정 등 강점을 보이는 부분이 있다. 바닐라 Vim은 아쉽고, Conf를 만들기에는 비효율적이고… 이런 것을 손쉽게 하기 위해 LazyVim를 사용해보자.</p><p>Neovim Config Framework 중 왜 LazyVim를 선택했는지, 어떤 구조인지 알아보자.</p><p>또한, LazyVim을 바로 사용할 수 있도록 가이드 내용도 포함한다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="neovim-config-framework-비교">Neovim Config Framework 비교<a class="hash-link" href="#neovim-config-framework-비교" title="제목으로 바로 가기">​</a></h2><p>찾아보면 <a href="https://github.com/lunarvim/lunarvim" target="_blank" rel="noopener noreferrer">LunarVim</a>, <a href="https://github.com/AstroNvim/AstroNvim" target="_blank" rel="noopener noreferrer">AstroNvim</a>, <a href="https://github.com/LazyVim/LazyVim" target="_blank" rel="noopener noreferrer">LazyVim</a>가 주로 사용되는 듯하다.</p><p>GitHub Star 수로 비교하면 다음과 같다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAo0lEQVQYlT2Q2apDMQhF8/8/WfpwnsLtkDlxWCW5pYIIuvdCDapGa43eO+5OKYWUEv/hqDliTtjN2/3OdV3MOb8Cjmmo03WLhbAJIusMdvKtTZxljq96MsQYMbMfycyp4oga3gs28zGGnDOqekR7l6bgm9Qrbv0HCHUfMgZtCXUKOgdSCzILqgvbZDeCiPBMhec7Mcbg9fgjp0TOiVIr8RHPVz7BxBHzAYTbCwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1832" height="1308"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.c633072.1832.png" srcset="/assets/ideal-img/1.c633072.1832.png 1832w" width="1832" height="1308"></noscript></div></div><p>역사는 LunarVim가 제일 오래되었고, 가파른 성장세를 보이는 것은 LazyVim이다.</p><p>보통 늦게 출시한 것이 더 발전된 기술을 사용하고 이전의 것들을 개선하는 경우가 많다. 게다가 엄청난 속도로 LunarVim을 따라잡은 LazyVim이 눈에 들어왔다.</p><p>그래도 역시 직접 사용해 봐야지 알지 알겠는가? 사용 후 비교는 아래와 같다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="lunarvim-vs-astronvim-lazyvim">LunarVim VS AstroNvim, LazyVim<a class="hash-link" href="#lunarvim-vs-astronvim-lazyvim" title="제목으로 바로 가기">​</a></h3><p>AstroNvim, LazyVim는 설치하는 UI가 나오면서 알아서 해주고 뭔가 모던하다는 느낌을 받았다. 왜 그런지 보니까 <a href="https://github.com/folke/lazy.nvim" target="_blank" rel="noopener noreferrer">lazy.nvim</a> 기반이라서 그렇다. 또한, 프로젝트가 시작된지도 얼마 안되었더라</p><p>그에 반해 LunarVim은 딱딱한 Terminal UI 이었다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="astronvim-vs-lazyvim">AstroNvim VS LazyVim<a class="hash-link" href="#astronvim-vs-lazyvim" title="제목으로 바로 가기">​</a></h3><p>LazyVim이 더 유려한 UI/UX를 제공했다.</p><ul><li><code>command</code> 화면도 명령 팔레트 형식으로 나오고</li><li><code>space</code> 으로 나오는 명령 화면도 세로 표시</li><li>시작 화면에서 손쉽게 config 수정 가능</li><li>Markdown에서 a 링크는 축소되어 표시</li><li>부드러운 스크롤 이동</li><li><code>cd</code> 으로 경로로 이동하지 않고 <code>nvim {경로}</code> 으로 하더라도 마치 프로젝트 <code>open</code> 하는 것 처럼 파일 탐색기 표시</li></ul><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="lazyvim으로-결정">LazyVim으로 결정!<a class="hash-link" href="#lazyvim으로-결정" title="제목으로 바로 가기">​</a></h3><p>최종적으로 LazyVim으로 결정했다.</p><ul><li>플러그인 관리가 나름 핵심인데 그 핵심의 <code>lazy.nvim</code> 이고 해당 개발자가 만든 것이 <code>LazyVim</code> 이라서</li><li>GitHub Star 많고, UI 모던하고, 기능 지원이 빵빵해서 (커뮤니티 파워 기대)</li><li>사용자 설정에 맞게 <code>config</code> 지정 가능</li><li>VSCode 통합 기능 제공 (config 통합 관리 가능 🤤)</li><li>기타<ul><li>기본적으로 필요한 플러그인이 이미 설치되어 있고 여러 플러그인 조합에서 단축키가 충돌하지 않게 잘 설정되어 있음</li><li>오픈소스로 누군가에 의해 계속 관리됨</li><li><code>space</code> 와 <code>?</code> 로 손쉽게 단축키를 찾을 수 있다. 단축키는 자연스럽게 까먹을 수 있는데 언제든 단축키를 살펴볼 수 있어 편리하다.</li><li>기본적인 설정이 되어 있다. 클립보드 연동이나 <code>Y</code> 입력 시 커서 뒤 라인 복사 같은 기본 유틸 키 맵핑이 되어 있어 편함</li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="lazynvim-이란">lazy.nvim 이란?<a class="hash-link" href="#lazynvim-이란" title="제목으로 바로 가기">​</a></h2><p>LazyVim 사용 전 lazy.nvim이 무엇인지 짚고 넘어가는 것이 좋을 것 같다. 필자는 처음 알아볼 때 lazy.nvim과 LazyVim의 역할에 대해 혼란스러웠다.</p><p>lazy.nvim은 플러그인 관리자로 Vim을 사용하다 보면 여러 플러그인을 사용하게 되는데 이를 손쉽게 관리할 수 있도록 도와준다.</p><p>쉬운 관리가 꽤 중요한 부분인데... Vim의 conf를 만진다는 것은 대부분 플러그인을 설치하고 관리하는 과정이다. 설치뿐 아니라 업데이트, 에러는 없는지 등 어떻게 보면 Vim을 사용하면서 많이 마주치는 부분이다. 이런 기능을 <code>TUI</code> 로 손쉽게 제공한다.</p><p>자동 설치 기능이 있어서 아주 편리한데, 보통 Vim 플러그인 매니저는 플러그인 매니저도 수동으로 설치해주고 플러그인도 명령어로 일일이 설치, 업데이트 해야 했는데 해당 제품은 플러그인 매니저 설치도 자동이고 업데이트도 <code>TUI</code> 로 손쉽게 가능하다. 현대적인 <code>IDE</code> 를 쓰는 기분을 느낄 수 있다.</p><p>이름에서 볼 수 있듯이 <code>lazy</code> 기능이 있어 필요한 시점(e.g. 명시적인 호출 시, 특정 키 입력 시 등)에 플러그인을 로드한다. 덕분에 플러그인 범벅인 Vim을 빠르게 사용할 수 있다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="lazyvim-시작">LazyVim 시작<a class="hash-link" href="#lazyvim-시작" title="제목으로 바로 가기">​</a></h2><p>LazyVim은 각종 config만 제공한다. 그것을 설치/관리하는 주체가 lazy.nvim이고 config 관리는 <a href="https://github.com/LazyVim/starter" target="_blank" rel="noopener noreferrer">starter</a>로 역할이 나뉘어 있다.</p><p>시작 방법은 문서화가 잘 되어 있기에 <a href="https://www.lazyvim.org/installation" target="_blank" rel="noopener noreferrer">공식 문서</a>를 참고하면 된다. 먼저 Docker로 시작하기를 추천하는데, 이 방법으로 어떤 의존성이 있는지 한눈에 파악할 수 있다.</p><p>또한, 필요한 의존성이 있을 수 있는데 <a href="https://www.lazyvim.org/#%EF%B8%8F-requirements" target="_blank" rel="noopener noreferrer">Requirements</a>를 확인하면 된다. 문제가 된다면 거의 <code>Nerd Font</code> 최신 버전 미설치로 인한 폰트 깨짐이나 <code>fzf</code> 미설치 정도다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="lazyvim-기본적인-사용법">LazyVim 기본적인 사용법<a class="hash-link" href="#lazyvim-기본적인-사용법" title="제목으로 바로 가기">​</a></h2><p>기능이 워낙 많다 보니 처음에는 어디서부터 시작해야 할지 막막할 수 있다. 아래 내용을 참고하면 전체 흐름을 이해하고 빠르게 적응하는 데 도움이 될 것이다.</p><p>내용은 우선순위에 맞게 배치해 보았다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="주요-개념">주요 개념<a class="hash-link" href="#주요-개념" title="제목으로 바로 가기">​</a></h3><ul><li><code>&lt;leader&gt;</code> 는 <code>space</code> 이다.</li><li>첫 설치 시 <code>lua/plugins/example.lua</code> 의 경우 <code>if true then return {} end</code> 조건으로 실행 안되는 상태다.</li><li><code>lua/plugins/*.lua</code> 파일은 논리적인 단위다. <code>ui.lua</code> 라고 만들고 <code>UI</code> 관런 플러그인을 몰아 넣을 수도 있고 플러그인 단위로 파일을 만들어서 관리할 수도 있다.</li><li><code>return {}</code> 으로 반한되어야 함</li><li><code>lua/config</code> 경로에서 기존 구조 이외 파일은 로드 되지 않는다.</li><li>LazyVim의 설정은 <code>lazyvim.json</code> 에서 관리된다.</li></ul><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="lazyextras">LazyExtras<a class="hash-link" href="#lazyextras" title="제목으로 바로 가기">​</a></h3><p>LazyVim 설치 시 기본적인 플러그인은 활성화가 되어있지만 유틸적으로 제공하는 기능은 사용자가 추가 활성화 해야한다. like. <code>LazyVim</code> 의 <code>확장</code> 이라고 생각할 수 있다.</p><p>필자는 분명 문서에 있는 기능인데 안되어 해맸다. 해당 내용을 우선 배치한 이유도 이걸 알아야 공식 문서의 내용을 넓게 이해할 수 있다.</p><p><a href="https://www.lazyvim.org/extras/lang/typescript" target="_blank" rel="noopener noreferrer">TypeScript</a> 를 예시로 구조를 살펴보면 <code>.lua</code> 파일은 <a href="https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/plugins/extras/lang/typescript.lua" target="_blank" rel="noopener noreferrer">lua/lazyvim/plugins/extras/lang/typescript.lua</a> 에 정의되어있다.</p><p><code>.lua</code> 구조를 보면 아래와 같은데 여러 플러그인의 설정 모음인 것을 볼 수 있다.</p><div class="language-lua codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">~/.local/share/nvim/lazy/LazyVim/lua/lazyvim/plugins/extras/lang/typescript.lua</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-lua codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    recommended </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> LazyVim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">extras</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">wants</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">...</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">end</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"neovim/nvim-lspconfig"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    opts </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">...</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"mfussenegger/nvim-dap"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    opts </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">...</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"echasnovski/mini.icons"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    opts </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">...</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>공식 문서를 보면 해당 플러그인의 각각 섹션이 어떤 옵션으로 설정할 수 있는지 볼 수 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAABYlAAAWJQFJUiTwAAAARklEQVQImS3KMQ6AIAxAUe5/PGGTkdFSIho1tnyj8c0vqCpaBRFB28p5GTB4DXf2ptxbJ8w5M8VEjIlSyhfc/2hGl4VDKw88nkxqTEW81AAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1382" height="278"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.d59f236.1382.png" srcset="/assets/ideal-img/2.d59f236.1382.png 1382w" width="1382" height="278"></noscript></div></div><p>예시로 <code>mini.surround</code> 를 활성화 해보자</p><p><code>mini.surround</code> 는 <code>" "</code> , <code>{ }</code> 와 같이 괄호를 손쉽게 추가하고 제거하는 플러그인이다. 문서에는 LazyVim이 지원하는 것으로 나온다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAaElEQVQImU1OWw7AIAzq/c/qps7O1sWWRfeIJPwUCtAWD/TeMeDuP1kNhzjMpwSKKaO1C2b2XN6HzBUhMcqpEFFQazqFz/gkAqUUbCFg6CICWlMGPzCf2Pc4TaoKWnetqLUi5Txrx7QbpxPEXWnz+V0AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2512" height="1206"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.ace4afe.2512.png" srcset="/assets/ideal-img/3.ace4afe.2512.png 2512w" width="2512" height="1206"></noscript></div></div><p>활성화를 위해 <code>:LazyExtras</code> 입력해서 들어가면 <code>Recommended Plugins</code> 으로만 나오는 것을 확인할 수 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAaklEQVQImSXKWw6CMBBAURYi0w5tLcVoWt5EQoLG/S/pmujH+TvVVE7i7UDjExtWxC+IDoj2WM1YLT/VvXy4Pt5IOnDtRmhnJK5c0o6kDeuGfyz5RdftNH6h8RPqeowfMWGmdiO1KdQm8wWx7izWDIFfYQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3362" height="1116"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.f4f51e7.3362.png" srcset="/assets/ideal-img/4.f4f51e7.3362.png 3362w" width="3362" height="1116"></noscript></div></div><p>해당 플러그인 텍스트에 커서를 두고 <code>x</code> 를 눌러 활성화한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAMklEQVQImWMwM3b5b2Tg/F9D2+6/uo7Df01Ns//q6sb/5TUt/0vr2P+X1nX4L6Nm8R8ANtAPf4m3TUwAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2820" height="282"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.96dcfca.2820.png" srcset="/assets/ideal-img/5.96dcfca.2820.png 2820w" width="2820" height="282"></noscript></div></div><p><code>nvim</code> 을 다시 시작하면 <code>lazy.nvim</code> 에 의해 자동으로 설치되는 것을 볼 수 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAABYlAAAWJQFJUiTwAAAALElEQVQImWPQ1nP6r6rp8F9Jzfq/sqrFfyUQVrEAsxVVLf7Lq1v9V9Cw/g8AK6YPIUXW3/YAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2318" height="356"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.8777c1f.2318.png" srcset="/assets/ideal-img/6.8777c1f.2318.png 2318w" width="2318" height="356"></noscript></div></div><p><code>mini.surround</code> 추가하는 단축키는 <code>gsa</code> 인데 이전에 <code>g</code> 에서 보이지 않던 매뉴가 보여서 제대로 활성화 된 것을 볼 수 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAANCAYAAACQN/8FAAAACXBIWXMAABYlAAAWJQFJUiTwAAABQElEQVQokVWQ21KDMBRF+Y+2SSDcSSDcpUVs6Q2rrTP6/9+ynYRa9WHP5GHlnL2OxZIBXIyIwhdE8Q6uvwZxShCnuqcE5Q0sKgZEzQ198YWu/ISIRixJhhVV92Tmg0XDNXx1RpVeIJMDwmAAd59AWG6i4RmMewT5yYBKTmaiw5sZYH9AFnZI5BZpckQqjkiiEYHf/8L0DjpxjyqfUGbvaPIb2vwDSp7BefsfpP6TMS7TN7huB2ZXYHaJpZbQPakC/ZGJ5QGNukLEo6nged2j34JIELuERb0WYTggEyfkcjJR4mwuEEfb+QreGhbxWiTx3qwu5CtqdTWg7qilwuAZ3Otg+X6DPO7Rqguk2IPxxvTSHXUWJJ1XJ36NTbFDqia4vAKxC1C7eIg8rAmvwdwaK6cG4RVsv4MdbkB5bSbp6Pc3/AvFuLT+wcUAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1302" height="1670"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.a9d73d1.1302.png" srcset="/assets/ideal-img/7.a9d73d1.1302.png 1302w" width="1302" height="1670"></noscript></div></div><p>이렇게 추가된 <code>:LazyExtras</code> 는 <code>lazyvim.json</code> 에서 관리된다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAABYlAAAWJQFJUiTwAAAALElEQVQImQXBURYAEAhFQftfK6LOS/xcMy3DkQ0qgifx6rKPmOl4JeuIvo0PFo8mi+rJFR4AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1862" height="260"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.9a4e3e8.1862.png" srcset="/assets/ideal-img/8.9a4e3e8.1862.png 1862w" width="1862" height="260"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="단축키-보는법">단축키 보는법<a class="hash-link" href="#단축키-보는법" title="제목으로 바로 가기">​</a></h3><p>공식 문서 <a href="https://www.lazyvim.org/keymaps" target="_blank" rel="noopener noreferrer">https://www.lazyvim.org/keymaps</a> 를 보면 맵핑 정보를 모두 볼 수 있다.</p><p>여기에 있는 단축키가 바로 되는 것은 아니며 필요에 따라 <code>:LazyExtras</code> 으로 설치해야 작동하는 것이 있다.</p><p>e.g. <code>DAP Core</code></p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAZ0lEQVQImSXG7QrCIBhAYe//vvoXEdEIxkRY+TqdYytkfZidsA48cJQshUbetL5gpsKuz7jrh1oB6lVqnF+c3YoMiRgTR31jCAnyHZ7rX36gpu0GfzoQjSUY9+O0YDuhby0X7Qn7hi9p3nD8zuPCuwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="299" height="90"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9.90f347a.299.png" srcset="/assets/ideal-img/9.90f347a.299.png 299w" width="299" height="90"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAXklEQVQImW3D2wqEIBRAUf//8zRFq6cYB5Iou0yNcWIHPbdgKWcdxhi8D2itERHeqM+yM/z+bAKrwHwU8rI95yLkIqznhXJ9JnwTPiaafqTtIlZbQmWpY6JNE8N+cgNBcXER0/DojwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="564" height="206"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/10.30d25a9.564.png" srcset="/assets/ideal-img/10.30d25a9.564.png 564w" width="564" height="206"></noscript></div></div><p>Vim에서 단축키를 확인하는 방법은 아래와 같다.</p><ul><li><code>&lt;space&gt;</code> ( = <code>&lt;leader&gt;</code> )<ul><li>Focus 된 곳에서 사용 가능한 단축키 보여줌</li><li>페이지 네이션이 되어있어 <code>back</code> 으로 뒤로 넘어가서 단축키 있는지도 보기</li></ul></li><li><code>?</code><ul><li>Focus 된 곳에서 사용 가능한 단축키 보여줌</li><li>플러그인 자체에서 제공하는 <code>help</code> 성격임</li></ul></li><li><code>?</code> 와 <code>&lt;leader&gt;</code> 차이<ul><li><a href="https://github.com/nvim-neo-tree/neo-tree.nvim" target="_blank" rel="noopener noreferrer">neo-tree.nvim</a> 예시로 보면</li><li><code>?</code> 는 플러그인 자체에 대한 단축키만 보여주고<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAcElEQVQImR2KQQrCMBAA8w0lWVNS4wa0xagVCVURxLMXURHx/58YaU8zDGO2tx9hfyeWB+n8RU8fFsf3yNg/R4/9C2NdQzVrUT0Q5x2bfMVLi9gldpqwE8W5BiN1h/g1EnYELaTVhVoLbmhVRnxmeP6EEC6skzKocQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1304" height="480"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/11.f9ed637.1304.png" srcset="/assets/ideal-img/11.f9ed637.1304.png 1304w" width="1304" height="480"></noscript></div></div></li><li><code>&lt;leader&gt;</code> 는 첫 페이지에 안나오고 <code>back</code> 으로 뒤로 가야 보여주는데 플러그인의 명령을 LazyVim에서 어떻게 맵핑되었는지 보여준다.<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAMklEQVQImWNQ1LT+r6Jh9V9d2+G/rW3AfzNtu/+amjb/VY09/qtoO/xXUrH4L69k8h8AN1MPrdu3DU4AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="854" height="126"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/12.d7a2ec8.854.png" srcset="/assets/ideal-img/12.d7a2ec8.854.png 854w" width="854" height="126"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAPCAYAAADd/14OAAAACXBIWXMAABYlAAAWJQFJUiTwAAABSklEQVQokWVS226DMBTrhxSScL/mQlIKWykVq7YJ2nX//zOeSFVWtgdLkY7lYztnw4oevjrDyw/I8w5h3GHLKjiehuuZBRvi7+D6BkFxwF4N0PyCIh2QRB0cIuASCYdIbFxP24eXNNCihypH8PyMMGzhUmVnDrVEA4cq+GkLI3oY8QUtRqjyExUfkcbHh+KdOCuqsoMRV2gxocje4Pk12OyRqjWx4j20uMCICVlysoSty9eKLK6xkwOydIBL5aJGZzD9pJi1lljxCbL4gOaT9ZjERwRB+0RMW7u6Vt/WY5r0cGmFLREWq9RzPTt5Qxwd7HDucfa3DpM2qOWwKBJagTBt8aeePYw8QYsrjLigVje7fg71r8fHas1HqyqKd5v8lzh/YdxAlkdIPqIoziCsAmX3su/EoLbXQaIGLHoBjRrQ9BU0bEGC/XI9P1wH4/M74kGIAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1116" height="1634"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/13.e824ab4.1116.png" srcset="/assets/ideal-img/13.e824ab4.1116.png 1116w" width="1116" height="1634"></noscript></div></div></li></ul></li></ul><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="주요-명령어">주요 명령어<a class="hash-link" href="#주요-명령어" title="제목으로 바로 가기">​</a></h3><ul><li>LazyVim<ul><li><code>:LazyHealth</code><ul><li><code>LazyVim</code> 설치 및 실행 후 상태 확인</li></ul></li><li><code>:Lazy sync</code><ul><li>다시 로드</li></ul></li></ul></li><li>LSP<ul><li><code>:LspInfo</code><ul><li>어떤 것 로드 되었는지 확인 가능</li></ul></li></ul></li><li>mason.nvim<ul><li><code>:Mason</code><ul><li>Dashboard 확인</li></ul></li></ul></li></ul><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="주요-플러그인">주요 플러그인<a class="hash-link" href="#주요-플러그인" title="제목으로 바로 가기">​</a></h3><ul><li><a href="https://github.com/nvim-neo-tree/neo-tree.nvim" target="_blank" rel="noopener noreferrer">neo-tree.nvim</a><ul><li><code>r</code> 이름 변경</li><li><code>d</code> 파일 제거</li><li><code>h</code> 숨김 파일 보기 토글</li><li><code>&lt;</code> , <code>&gt;</code> 으로 탐색 화면 돌아다닐 수 있음<ul><li>그 중 <code>GIT STATUS</code> 의 경우 <code>vim</code> 으로 열린 <code>Git</code> 의 변경된 파일 내역을 보여줌<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAMklEQVQImWMwNPP7b2fl+9/GOvC/tbnXf2NT7//6pv7/9U18/+uZ+P43MvP/b23q/B8AVWsRJKX3o9IAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1060" height="102"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/14.5cf78dd.1060.png" srcset="/assets/ideal-img/14.5cf78dd.1060.png 1060w" width="1060" height="102"></noscript></div></div></li></ul></li></ul></li><li><a href="https://github.com/folke/snacks.nvim" target="_blank" rel="noopener noreferrer">snacks.nvim</a><ul><li><code>&lt;leader&gt;gs</code> Git Status<ul><li><code>M</code> 이 <code>+</code> 있으면 stage <code>-</code> 면 unstage<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAT0lEQVQImS3I0QpAMBhAYa/C/pFlyL9WsmtKjVvl/Z/jiFyc+jqF+oWmDhgbvyozIa/riBH9X6DYXGKKJ+N8oenG6YkdDqTPSLcjPlO2Kw/bIh+Pu3lDRwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1628" height="316"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/15.135c190.1628.png" srcset="/assets/ideal-img/15.135c190.1628.png 1628w" width="1628" height="316"></noscript></div></div></li><li>순수한 화살표 입력으로 ← , → 하면 적용됨</li></ul></li></ul></li><li><a href="https://github.com/folke/flash.nvim" target="_blank" rel="noopener noreferrer">flash.nvim</a><ul><li><code>s</code> + {검색 글자}<ul><li><code>{검색 글자}</code> 검색된 결과가 나오는데 분홍색 키 입력 시 거기로 이동<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAcklEQVQImR3KSw6CMBRA0a4GXz9ICTaoEKQlIAOicaAJTE3c/w6u0dkZHCU24f2Cyyd+1jYhZiDTPaIjOx3J5IKSfGBs35zCE1POLP2Httko6wfNeeVYv3DFjMpspAsrB3/DupEubFTVHbuf/sEVV8QkvsLcMfw4IdTfAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="937" height="273"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/16.b49064f.937.png" srcset="/assets/ideal-img/16.b49064f.937.png 937w" width="937" height="273"></noscript></div></div></li></ul></li><li><code>S</code><ul><li>커서 근처의 괄호 영역을 보여주는데 분홍색 키 입력 시 해당 범위까지 <code>visual</code> 으로 잡아 줌<ul><li>아래의 상태에서<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAUklEQVQImQXBiwpAMABAUZ9DIoxmrIXMMw3J4/8/5DrHi0KLqHYytVGoCakXpF7J5IQoZ8LY4gcdXhpZyvYmrw+MWRi3j+H8aNeH3r2o5kInjh9koSLyJWscBwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1012" height="258"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/17.35cd024.1012.png" srcset="/assets/ideal-img/17.35cd024.1012.png 1012w" width="1012" height="258"></noscript></div></div></li><li><code>f</code> 누르면 아래와 같이 잡힘<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAWElEQVQImWPQNQr+b+Vb+9/at+q/tXv+fxuv/P+23vn/LVwywNjUIeW/lr7/fwZ9o5D/TrH9/x2i+v+7hLf9903q+B+S3fHfP7Xzf2BG93+vuMb/RlYR/wEgFSfk6Yt5ewAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="948" height="258"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/18.64419fa.948.png" srcset="/assets/ideal-img/18.64419fa.948.png 948w" width="948" height="258"></noscript></div></div></li></ul></li></ul></li></ul></li></ul><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="lspdap-언어-기능디버깅">LSP+DAP (언어 기능+디버깅)<a class="hash-link" href="#lspdap-언어-기능디버깅" title="제목으로 바로 가기">​</a></h3><p>Vim을 IDE처럼 사용하겠다면 꼭 알아야하는 기능이다.</p><p>여기서 <code>mason.nvim</code> 라는 새로운 패키지가 또 등장하는데... (머리 아파하지 말아야 쉽다!)</p><p>TL;DR</p><ul><li><code>mason.nvim</code> 는 <code>LSP</code> , <code>DAP</code> , <code>Linter</code> , <code>Formatter</code> 와 같은 언어 기능 플러그인을 손쉽게 관리할 수 있는 <strong>패키지 매니저</strong> 이다.</li><li>그렇기 때문에 <code>mason.nvim</code> 만의 <a href="https://mason-registry.dev/registry/list" target="_blank" rel="noopener noreferrer">패키지 저장소</a>가 있다.</li><li>설치 후 플러그인 설정은 Vim에서 해야함</li><li>우린 <code>LazyVim</code> 을 사용하니 이미 제공된 것 사용하면 됨</li></ul><p>예시로 <code>JavaScript</code> 코딩할 수 있도록 IDE 처럼 설정해보자. 우선, 테스트 파일을 생성한다.</p><div class="language-js codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-js codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> obj1 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"my-name"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">arg</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">99</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">obj1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>LazyVim <code>lang</code> 활성화 해야하는데 <code>JavaScript</code> 만을 위한 것은 없고 <a href="https://www.lazyvim.org/extras/lang/typescript" target="_blank" rel="noopener noreferrer">LazyVim Lang TypeScript</a> 으로 나와있다.</p><p><code>:LazyExtras</code> 에서 <code>lang.typescript</code> 활성화한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAL0lEQVQImWMwMPP8b2bl919P3+m/vJrVf3l16//y6iDa6r+8hs1/JXWL/zqaxv8BOS4PjyTD6nYAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1394" height="98"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/19.11b0f4b.1394.png" srcset="/assets/ideal-img/19.11b0f4b.1394.png 1394w" width="1394" height="98"></noscript></div></div><p>그러면 <code>JS</code> 에서 심볼 등 언어 기능이 동작하는 것을 볼 수 있다.</p><ul><li><code>&lt;leader&gt;c</code> 에서 여러 코드 옵션 볼 수 있음<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAYAAAALpr0TAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA+klEQVQYlV3Qy3KCQBBAUf4j4TGDMOjIIPKK8lCikljBRZL//5abYqqySFa9OX2rqx2ZHMj3ryT6RpwMuLLEFYWdXljhyRI/anCS3cDH+U6VfXIsvtgkJ6LVgTBscP0drp9Z7MRpz9CMGP1Otr1h9A0VdwhZ4QW5xUvZUabn0k7s0wdGX9msRwuePfMPZj2PcabOv9ltJ/R6tMVAlH9hqDuK4ozRb+TpnWw7WRhFR7xgj/cLA92j65kym0lUb5GUNU9eamvLCcsHHLnpqM1EYWZbWxa0vqBUhxAlKmoJ4xbHXzX4y6/CCqFapGqJsivhesCXFWL1QhAd+AGWwYpSFPpcowAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="906" height="794"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/20.a1201a9.906.png" srcset="/assets/ideal-img/20.a1201a9.906.png 906w" width="906" height="794"></noscript></div></div></li><li><code>&lt;lender&gt;cs</code> 으로 심볼 볼 수 있음<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAT0lEQVQImR3JQQqAIBQA0Y5SmqlUWiDyoWghtAhadv+rTOhqBl6nRsFuhXW/0FNGGyHIS5SPOT5U73Wmc/4gpQcfbtxSGCpYwbizvTLS+gPrZB8ToGfX9AAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1250" height="238"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/21.a5ef6d3.1250.png" srcset="/assets/ideal-img/21.a5ef6d3.1250.png 1250w" width="1250" height="238"></noscript></div></div></li></ul><p>다음은 <code>Debug</code> 으로 <a href="https://www.lazyvim.org/extras/dap/core" target="_blank" rel="noopener noreferrer">LazyVim DAP Core</a> 에 따라 <code>:LazyExtras</code> 에서 <code>dap.core</code> 활성화한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAUUlEQVQImSXKSwqAIABAwQ7SH6WsTCWwEoLou+r+x3lBLWY3UWt2aneT2xtlV7SeMdqjqoGisKTZLwr+wE3PF0tzItyF6DdktyCaQCJHYuF5AeluHxldsAXBAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2368" height="484"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/22.dae585e.2368.png" srcset="/assets/ideal-img/22.dae585e.2368.png 2368w" width="2368" height="484"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAMklEQVQImWPQN3L/r2vg8V/D0PO/mqHHf2Vd5//KWg7/FTVs/surW/1XVDL9r6hk8h8AOpgPmUtAI8MAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2622" height="288"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/23.b040d6f.2622.png" srcset="/assets/ideal-img/23.b040d6f.2622.png 2622w" width="2622" height="288"></noscript></div></div><p>그러면 <code>&lt;leader&gt;d</code> 에서 여러 디버깅 옵션이 활성화 된 것을 볼 수 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAOCAYAAAAWo42rAAAACXBIWXMAABYlAAAWJQFJUiTwAAABcElEQVQokVXS23bTMBCF4bwGtD4ltmRblg+JHMenuMYpkDZAWWUB7/8iP8tOC/Ri7j5pa2a0cuMeKSqy9J44f0CpEzr5iAwHLG+7lBMcWK2zCSn3pLKlKp4o8y9s9ZnD9js6PuF5O+zNnpWrR4qoZcg+0ZS/6Mqf1LtndDwhRMtmU2H7M1RHlKgo1UgYDnhrQ+DX2G7Bu1vFeyt5gemIDmsqNZKo++WGuUTQIkWHDFo80bBykzuKqKHTJ8riiV32iIpGRNBcoeheYHwkCvaY+I4oHJeoWzv9WzeWvkY7yUAW1rT6xDa7sE0fcD2D5eTLO20nx/ErVl42kUctTfIBU3zDZJelIcvJFjgfWOA88MA36LDD5F+J5BEp+iv6H3pqwFc9iZ7oy9805sfSwGv0Pxh2rNOeZH/G6Mdl0K/gLdQjcXSgSAaMeSZJP2PPa5uhk2PZGc4y8KhjvTH4cYefnhBqQh4uyN0Zd7PHnj+FqPkDOPjXRgUYaR0AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="624" height="914"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/24.7a51f26.624.png" srcset="/assets/ideal-img/24.7a51f26.624.png 624w" width="624" height="914"></noscript></div></div><p><code>&lt;leader&gt;db</code> 으로 BreakPoint 걸고</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAABYlAAAWJQFJUiTwAAAArElEQVQImR3KwWrCMBzA4bzGDnazTdMmKqu0if03NFpQGDhkoCibF59g73/7jXn4bp/K9Ya8FCoT8f6ThRxZ90e6+MHKTzQ+oWtB/cfCDBR5IGsnXvZfdM1ICCOuiTRhpLQRNSs2zHWPNgOvdSSrhLoM6LxjlrVPb/MOFeqAMz2VS8hwYmm3GLejcDvscnoqbUJd257oAu/rA4+fXw5yQeSMTXdk+42kGyt/4g9EYlAgpm/rsgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="466" height="244"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/25.31f5a1b.466.png" srcset="/assets/ideal-img/25.31f5a1b.466.png 466w" width="466" height="244"></noscript></div></div><p><code>&lt;leader&gt;da</code> 으로 시작할 수 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAzklEQVQImT3Hy0rDQABA0fkPS56teUwm0SqxSSaNM00fq4IptdCFG/0BwY24ETeCy/7wFSq4OIsjnKjBL5b4xQo/7/GU/buyuGlHZQ5MZIcYKUM4PxBUD0zWz4T1QKj3hM0O52rF8ekNmbWIZFxzk/UoeU+eGZQ0qMyQK8v0eolMO6Q0COutMe0HhX2nG74YXk5sjj8020/6x++zpH1FuOEdUWKI0wXT2ZbZfE+pd0TScOHe4gTlmRiNS7xEE8QaL64JUk2QtnhRjXtZ/fsF7yhj3BPhn94AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="352" height="216"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/26.77cc1ad.352.png" srcset="/assets/ideal-img/26.77cc1ad.352.png 352w" width="352" height="216"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAIElEQVQImWPQ17X8r6lpAcZaWhb/QXw9JDEQ1tay/A8AMdIPgfTSzb0AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1172" height="128"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/27.9a425bc.1172.png" srcset="/assets/ideal-img/27.9a425bc.1172.png 1172w" width="1172" height="128"></noscript></div></div><blockquote><p>빈 값 지정</p></blockquote><p><code>VSCode</code> 디버깅 레이아웃과 유사하게 왼쪽에서 local 스코프, 현재 메모리 상태 등 UI 를 확인할 수 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA1UlEQVQImTWNy2oCMRiF8yhN/j+TxMnMpIpxLlYdKW0pXRQfwJ3gwzibtvgkupB2VboaZuu7iDBEJtrFx7lw4JC+ff4YDF+Pyf281vGk0dFDo6Nxo3p5o8KiVr38qOPxlqAofoSaOAwyx9A6ClfYjc4HMvslMp7uRTjrijMF2zIctQxte/W+64bfJNTlLowfHeXZieHo/A8Fr6c7sI6L9ECUyP+AZ44Fqb8HnnrQ59RJUbgomdfkfbZYJebpi0K0ATQVcFMxNJVUppIi2ZT98vPlbbm+AHMwUtb86iX8AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3808" height="2246"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/28.0fb41b9.3808.png" srcset="/assets/ideal-img/28.0fb41b9.3808.png 3808w" width="3808" height="2246"></noscript></div></div><p><code>&lt;leader&gt;dO</code> 으로 <code>Step Over</code> 하면 <code>console.log</code> 가 실행되어 변수가 출력되는 것을 볼 수 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAoklEQVQYlXWOyw6CMBRE+ydIX7blZRNKKYoiLnxt+P+fGUODhIUuTnLn5iQzJHMv+HqCdW9k5Yj8MMDkZwgZIPYBXPgIkdkFRg8wxRXK9PE5C2wRVpHpHlL3YLID5S1S1iz4zd2ApLzBjrkY6Az3PyFleKI+TtDVHVSGvzLJixG2ekAVN1DRIqEOyaZyrZasBecBKQ9QqoOtBlh9Apdd3P3lA9N3arTjdgBCAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2130" height="1494"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/29.22d2e44.2130.png" srcset="/assets/ideal-img/29.22d2e44.2130.png 2130w" width="2130" height="1494"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="vscode-통합-기능">VSCode 통합 기능<a class="hash-link" href="#vscode-통합-기능" title="제목으로 바로 가기">​</a></h3><p>놀라운 것은 이 <code>Config</code> 를 <code>VSCode</code> 에서도 사용할 수 있다는 것이다.</p><p><code>Vim</code> 이 은총알(silver bullet)은 아니기에 <code>Vim</code> 을 좋아한다면 상황에 맞게 <code>VSCode</code> 에서는 <a href="https://github.com/vscode-neovim/vscode-neovim" target="_blank" rel="noopener noreferrer">vscode-neovim</a> 확장, JetBrains는 <a href="https://github.com/JetBrains/ideavim" target="_blank" rel="noopener noreferrer">IdeaVim</a> 플러그인을 이용할 것이다.</p><p>VSCode에서 거의 네이티브로 지원되는 vscode-neovim 확장에서 사용할 수 있도록 기능을 제공한다. <a href="https://github.com/vscode-neovim/vscode-neovim?tab=readme-ov-file#neovim-configuration" target="_blank" rel="noopener noreferrer">해당 확장 문서</a>에도 명시되어있다. 덕분에 LazyVim, VSCode 따로 conifg를 관리하지 않아도 된다. 또한, LazyVim 기능 중 VSCode에서 사용가능한 것만 활성화된다.
어떤 기능이 활성화 되는지는 <a href="https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/plugins/extras/vscode.lua" target="_blank" rel="noopener noreferrer">vscode.lua</a> 를 살펴보면 된다.</p><p>사용 방법은 <code>:LazyExtras</code> 으로 활성화하고, VSCode에서 <code>vscode-neovim</code> 확장을 설치해 사용하면 된다. VSCode에서 Neovim이 실행되는 경우 <code>vim.g.vscode</code> 변수가 할당되는데 해당 변수 값 유무에 따라 플러그인이 자동으로 활성화 된다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>Vim을 쓴다는 것부터 어느정도 고인물이라는 가정으로 문서화가 되어있다. 그래서 사용하려면 이해하는데 시간이 꽤 걸린다.</p><p>IDE 대신 Vim에 입문해보고 싶을때 나름의 진입 장벽이 있는데 이 글을 통해 조금의 도움이 되었으면 좋겠다.</p>]]></content>
        <category label="Vim" term="Vim"/>
        <category label="Neovim" term="Neovim"/>
        <category label="Neovim Config Framework" term="Neovim Config Framework"/>
        <category label="lazy.nvim" term="lazy.nvim"/>
        <category label="LazyVim" term="LazyVim"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[MS가 만들어준 어떤 에디터에서도 통하는 언어 기능]]></title>
        <id>/2025/02/01/editor-universal-language-features-ms</id>
        <link href="https://parkgang.github.io/blog/2025/02/01/editor-universal-language-features-ms"/>
        <updated>2025-02-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[VSCode를 개발하면서 LSP, DAP 등 별의별 것 다 생겼구나]]></summary>
        <content type="html"><![CDATA[<p>VSCode를 개발하면서 LSP, DAP 등 별의별 것 다 생겼구나</p><p>그냥 사용자 입장이면 모르고 지나갈 수 있지만, 여러 개발 환경을 구축하며 깊게 파고들다 보니 많은 것을 경험하게 되었다.</p><p>특히, <code>PHP</code> 디버깅을 위해서 바닥부터 구현한 <code>Xdebug</code> , <code>Neovim</code> 에서 여러 언어 기능을 활성화하면서 많이 배웠다.</p><p>사실상 <code>VSCode</code> 가 되면 다른 IDE(더 나아가 Vim)도 잘 된다는 생각이 들었다.</p><p>이렇게 보니까 <code>JetBrains</code> 가 독자적으로 얼마나 대단한가 생각이 들었다...</p><p>참조, 에러 등 기본적인 언어 기능에 비해 더 고급 기능이라고 볼 수 있는 <code>리팩토링</code> , <code>분석</code> 기능은 모든 언어가 손쉽게 지원하지 않는다는 것을 알았고 지원하더라도 이걸 잘 해주는 도구가 <code>JetBrains IDE</code> , <code>Visual Studio</code> 같은 것이고 그렇기에 유료 툴이 의미가 있구나 느꼈다.</p><p>또 MS에서 <code>LSIF(Language Server Index Format)</code> 만드는 것을 보고 놀랐다. 해당 기능은 코드 분석을 사전에 만들어서 사용하는 것인데 웹에서 코드 볼 때 유용하게 사용될 수 있다.</p><p>위 기능을 만들며 웹에서 언어 기능을 통해 손쉽게 코드리뷰 할 수 있다는 내용을 보았다. 다들 PR을 위해 Checkout을 귀찮아하는구나 공감도 하고 그걸 바꾸려고 이런 프로토콜도 제시하고 하다니… 사실 GitHub, Azure DevOps 사용하면서 웹에서 어느 정도 언어 기능 지원될 때 그냥 그렇구나 하고 넘겼는데 이런 기술을 보고 나니까 대단하더라…</p><p>그냥 사용만 했다면 모르고 지나쳤을 내용인데 바닥부터 개발 환경을 만지다 보니 이런 것을 배우게 되는구나.<br>
<!-- -->MS 참 많은 것을 바꿔주었구나.</p>]]></content>
        <category label="MS" term="MS"/>
        <category label="개발 환경" term="개발 환경"/>
        <category label="Tool" term="Tool"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[요즘 핫한 생산성 앱은 왜 macOS First일까?]]></title>
        <id>/2025/01/29/productivity-apps-macos-first</id>
        <link href="https://parkgang.github.io/blog/2025/01/29/productivity-apps-macos-first"/>
        <updated>2025-01-29T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Windows VS macOS 어떤 것을 선호하나요?]]></summary>
        <content type="html"><![CDATA[<p>Windows VS macOS 어떤 것을 선호하나요?</p><p>서로 장단점이 있지만 나는 유려한 UI/UX, like. UNIX 라 macOS가 편하다.</p><p>최근 Mac mini를 새롭게 셋업하면서 새로운 생산성 앱을 다운로드 받아서 보고 있는데 여기서 느낀 나름의 macOS 가 생산성 OS으로 각광 받을 수 있는지 느낀다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="요즘-씬에서-핫한-앱은">요즘 씬에서 핫한 앱은?<a class="hash-link" href="#요즘-씬에서-핫한-앱은" title="제목으로 바로 가기">​</a></h2><p>ChatGPT Client Program, Arc Browser, Raycast 앱 특징은 씬에서 주목받는 막 나온 따끈 따끈한 생산성 앱이라는 것이고, 두번째는 macOS First 지원이라는 것이다. 설령 Windows 를 지원하더라도 아직 미출시되거나 기능이 부족하다는 평가를 받는다.</p><p>사실 이것 이외 Notion Calendar, Notion Mail 도 Apple First 다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="왜-macos-first-일까">왜 macOS First 일까?<a class="hash-link" href="#왜-macos-first-일까" title="제목으로 바로 가기">​</a></h2><p>왜 그런 것일까? 단순하게 OS가 다르니까 그렇다는 생각이 들면서도 우리가 많이 사용하는 앱은 대부분 Windows 우선 지원 혹은 크로스 플랫폼으로 잘 나오고 있다.</p><p>그런데 내가 이 3개의 앱을 사용해보니 알겠더라…! macOS가 빌트인된 생산성적인 기능을 제공하고 있더라.</p><ul><li>ChatGPT<ul><li>스팟라이트처럼 호출하는 UI가 space 눌러서 나오는 미리보기 UI와 같음</li><li>이미 있으니 재사용할 수 있고 UI 통일성으로 예쁘기까지 함</li><li>베타로 출시된 협업 기능도 macOS 레벨에서 권한 지정이 잘 되어서 <code>.app</code> 레벨의 특정 텍스트만 추출할 수 있도록 잘 디자인된 것을 볼 수 있음</li></ul></li><li>Arc<ul><li>Swift로 만들어져서 그런가? macOS 특유의 부드러움이 느껴짐</li><li>여기서도 로컬 스토리지에 있는 파일 볼 때 미리보기 UI가 재사용됨</li></ul></li><li>Raycast<ul><li>파일 검색 중 디테일을 보거나 미리보기 UI 재사용됨</li><li>Focus로 특정 앱이나 URL 들어갈 때 차단 기능도 훌륭하게 지원됨<ul><li>OS에서 잘 제공되는 듯 (스크린 타임 응용?)</li></ul></li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="종합해서-보면">종합해서 보면<a class="hash-link" href="#종합해서-보면" title="제목으로 바로 가기">​</a></h2><ul><li>권한 설정이 심플하게 잘 되어있어 앱 설계가 편해 보였고</li><li>이미 macOS에서 제공하는 기능을 붙이면 되는 정도라 UI/UX, 심미성 등 유려하게 조합되고 이는 애플의 디자인 가이드대로 끼워넣기 좋음</li></ul><p>애플이 레거시에 나름...? 가차 없는 것으로 아는데 오히려 그런 것이 없으니 이렇게 생산적인 앱들이 손쉽게 나오는 것이 아닐까 싶다.</p>]]></content>
        <category label="macOS" term="macOS"/>
        <category label="생산성" term="생산성"/>
        <category label="Apple" term="Apple"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[이메일 관리법: 이메일 기능 분석]]></title>
        <id>/2025/01/28/email-inbox-zero</id>
        <link href="https://parkgang.github.io/blog/2025/01/28/email-inbox-zero"/>
        <updated>2025-01-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[읽지 않은 메일이 일정 수준을 넘어가면 읽는 것을 포기해버린다. 쏟아지는 메일을 어떻게 관리할 수 있을까? 여러 메신저가 등장해도 메일의 중요성은 떨어지지 않는다. 메일을 잘 관리할 수 있는 방법을 알면 특별한 능력을 가지게 될 수 있지 않을까?]]></summary>
        <content type="html"><![CDATA[<p>읽지 않은 메일이 일정 수준을 넘어가면 읽는 것을 포기해버린다. 쏟아지는 메일을 어떻게 관리할 수 있을까? 여러 메신저가 등장해도 메일의 중요성은 떨어지지 않는다. 메일을 잘 관리할 수 있는 방법을 알면 특별한 능력을 가지게 될 수 있지 않을까?</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="tldr">TL;DR<a class="hash-link" href="#tldr" title="제목으로 바로 가기">​</a></h2><ul><li>Outlook을 이용하여 여러 메일 관리</li><li>Inbox Zero 방식으로 받은편지함을 모두 비우고 처리할 메일은 Flag 지정</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="메일의-중요성">메일의 중요성<a class="hash-link" href="#메일의-중요성" title="제목으로 바로 가기">​</a></h2><p>카톡, 팀즈와 같은 메신저가 등장해도 현대 사회에서 메일은 중요하다. 메일은 공식적인 소통의 수단으로 자리잡고 있는데 나는 이걸 <code>보고서</code> 형태의 커뮤니케이션이라고 생각한다.</p><p>이것 이외에도 SaaS 등 여러 알림도 메일로 이뤄져 있어 놓칠 수 없다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="메일-관리의-어려움">메일 관리의 어려움<a class="hash-link" href="#메일-관리의-어려움" title="제목으로 바로 가기">​</a></h2><p>나는 여러 소식을 듣기 위해서 뉴스레터 등 메일링 서비스를 구독하고 있다. 늘 시간이 많고 메일을 읽을 수 있는 환경이면 좋겠지만 어느 순간 읽지 못하는 상황이 발생하고 그 순간부터 읽지 않은 메일이 쌓이기 시작한다.</p><p>읽지 않은 메일이 쌓이면 쌓일수록 포기해버리고 메일을 기피하게 되는 경험을 하게 되었다. 실수로 메일을 눌러 읽음 처리가 되면 불편했다. 난 지금 읽고 싶지 않은데... 수많은 메일 속에서 중간에 읽음 처리가 되어버리니 이빨이 빠진 느낌이었다.</p><p>또, 여러 메일 계정으로 흩어져 관리 비용이 큰 것도 걸림돌이었다. 하나로 합치는 여러 방법도 있지만 더 복잡해질 것 같았다.</p><p>개인적으로 느낀 메일 서비스, 프로그램은 고인물 프로그램 성격도 한몫했다. 상대적으로 UI가 직관적이지 않다고 느껴져 서비스와 프로그램을 이해하는 데도 시간이 꽤 걸렸다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="메일-프로그램의-복잡성">메일 프로그램의 복잡성<a class="hash-link" href="#메일-프로그램의-복잡성" title="제목으로 바로 가기">​</a></h2><p>유난히 메일 프로그램이 다른 프로그램과 비교하여 고려할 부분이 많았다.</p><ul><li>동기화</li><li>검색</li><li>알림</li><li>PC와 모바일 제공 기능이 다름</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="여러-메일을-한-곳에서-보기-위한-방법">여러 메일을 한 곳에서 보기 위한 방법<a class="hash-link" href="#여러-메일을-한-곳에서-보기-위한-방법" title="제목으로 바로 가기">​</a></h2><ul><li>메일 클라이언트 프로그램 사용<ul><li>웹 메일과 같이 강력한 기능 제공 안됨</li><li>동기화, 메일 알림과 같은 기초 설정이 아쉬움</li></ul></li><li>하나의 메일 계정으로 포워딩시켜서 보기<ul><li>가장 깔끔하지만 받는 메일 계정, 포워딩 받는 계정 2곳에서 메일 용량이 늘어나고 관리 포인트가 2개됨</li><li>트러블 슈팅 시 2개의 계정을 찾아봐야 하기 때문…</li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="메일의-기본-기능">메일의 기본 기능<a class="hash-link" href="#메일의-기본-기능" title="제목으로 바로 가기">​</a></h2><p>수신, 송신이 기본적인 기능이다. 더 세분화하면 회신, 전체회신, 전달 등 나뉠 수 있는데 이는 메일 작성과 관련된 것으로 스킵하겠다.</p><p>여러 메일 서비스에서 제공하는 기능 중 공통적으로 제공되며 메일 관리에 도움이 되는 기능으로 <code>Flag</code>가 있다. 이것도 참 헷갈리는 요소인데 어디는 별표, 어디는 중요 이런 식으로 이름이 다르다. 그래서 나는 한동안 이 기능이 각 서비스마다 다른 기능으로 생각했는데 동일하게 메일에 대한 <code>Flag</code> 처리를 해주는 기능이었다.</p><p>메일 서비스인 Naver, Gmail와 메일 프로그램 Outlook, Mail.app을 비교해보니 메일의 공통된 주요 기능을 볼 수 있었다.</p><ul><li>메일 코어<ul><li>송신, 수신, 회신, 전체 회신, 전달</li></ul></li><li>메일 분리<ul><li>중요 메일 지정 및 분리</li><li>Flag 지정 (즐겨찾기)<ul><li>메일 서비스에서 공통적으로 즐겨찾기에 해당하는 것</li><li><code>Gmail</code>에서 별표 표시 (중요 아님!)</li><li><code>Outlook</code>에서 Flag 지정 후 완료하면 Flag가 사라진 것으로 동작함<ul><li>즉, Flag 지정하면 웹 메일에서 <code>중요</code>로 표시 및 Outlook에서 Flag 있는 메일로 조회되지만 Flag 완료 처리하면 <code>중요</code> 표시 제거 및 Outlook에서 Flag 있는 메일로 조회 안됨</li><li>이처럼 완벽하게 <code>Flag</code>는 후속작업이 있는 처리해야 하는 일이고 <strong>완료 시 더 이상 볼 것 없는 Done으로 사용</strong>되고 있다.</li><li>기존 웹 메일 서비스는 Outlook의 Flag처럼 기간 지정 및 완료가 없어서 단순하게 중요하게 생각하는 메일을 <code>Like</code> (즐겨찾기) 처리하는 것 같았지만 실제로 그렇지 않았다.</li></ul></li></ul></li><li>보관<ul><li><code>Gmail</code>, <code>Outlook Client Program</code> 등 보편적인 메일에서 제공하는 기능</li><li>더 이상 <code>처리</code>될 것이 없는 이름 그대로 <code>보관</code>될 메일들</li><li><code>New(InBox)</code> → <code>InProgress(회신 등 부가 작업)</code> → <code>Done(보관)</code>의 흐름 중 마지막 단계로 볼 수 있다</li><li>Inbox Zero를 위해서 중점으로 사용될 기능</li></ul></li><li>정크</li><li>VIP 메일 주소</li></ul></li><li>부기 기능<ul><li>우선 순위 지정</li><li>나중에 보기 (리마인더, snoozed)</li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="메일-관리-전략">메일 관리 전략<a class="hash-link" href="#메일-관리-전략" title="제목으로 바로 가기">​</a></h2><ul><li>최대한 기본 기능만 사용해야지 다른 메일 서비스로 넘어가기 쉬움<ul><li>InBox으로 모두 모으기 (그래야지 따로 설정 안 해도 모두 알림을 받기 때문)</li><li>IMAP 기본 메일함 사용 (그래야지 전체 계정 공통되어서 조회 가능)</li></ul></li><li>메일 부가 설정은 웹 메일에서 최대한 설정하기</li><li>PC와 모바일은 다르다고 바라봐야 한다.<ul><li>PC<ul><li>주로 메일 보고 업무를 처리하는 환경</li></ul></li><li>모바일<ul><li>Push 알림을 적극적으로 보고 이동 중 미리 보는 등 Index 하는 성격이 강함</li><li>각 서비스의 네이티브 앱으로 Push 알림을 받기 (네이티브가 가장 잘 지원했음)</li></ul></li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="내가-사용하는-메일-관리법">내가 사용하는 메일 관리법<a class="hash-link" href="#내가-사용하는-메일-관리법" title="제목으로 바로 가기">​</a></h2><p>단순하고, 여러 메일 계정에서 문제 없이 사용할 수 있으며, 메일 서비스+메일 프로그램에 락인(Lock-in)되지 않는 방법은 <strong>받은편지함(Inbox) Zero</strong>였다.</p><p>받은편지함을 모두 비우면서 관리하는 방법이다.</p><p>나는 여러 메일 계정을 사용하고 있어서 아래와 같이 관리하고 있다.</p><ol><li>Outlook에 사용하는 메일 계정 추가</li><li>아침에 메일을 확인 후 나중에 처리가 필요한 것은 Flag 지정</li><li>읽은 메일은 보관 메일함으로 이동 (이 과정에서 받은편지함은 비워짐)</li><li>메일 중 Index 처리가 필요한 것은 따로 메일함을 만들어서 관리 (논리적인 그룹인데 보낸 곳이 여러 곳이라 검색으로 찾기 어려운 분류들 e.g. 여행 비행기 예약, 호텔 예약 등)</li><li>Flag 지정된 메일을 우선순위에 맞게 처리</li></ol><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="내가-오해한-메일의-기능">내가 오해한 메일의 기능<a class="hash-link" href="#내가-오해한-메일의-기능" title="제목으로 바로 가기">​</a></h2><ul><li>읽은 메일을 완료라는 생각<ul><li>메일을 읽으면 처리했다고 생각했다.</li><li>이는 안 읽은 메일함 속에서 읽은 것이 생겼기 때문이다.</li><li>메일을 읽은 것은 읽은 것이고 미처리된 것은 다른 메일함으로 분류를 하면 되는 것이었다.</li></ul></li><li>받는 사람, 메일링 리스트에 따라 메일 자동 분류<ul><li>반복적으로 오는 메일(e.g. 뉴스레터), 알림마다 자동 분류를 만들어서 관리했었다. 근데 이는 지속 불가능한 방법이었다.</li><li>성격 단위로 메일 보관함 나누는 것이 얼마나 힘드냐면 구독하는 메일이 늘어날 때마다 메일함+자동 규칙을 만들고 관리해야 하는데 서비스에서 자동 규칙 만드는 개수 제한도 있다.</li><li>메일을 받는 성격이 비슷하면 해당 주소로 필터링할 수 있기 때문에 메일함은 중요하지 않다. 그렇기에 서비스에서 메일을 보낼 때도 <strong><a href="mailto:no-reply@test.com" target="_blank" rel="noopener noreferrer">no-reply@test.com</a>, <a href="mailto:info@test.com" target="_blank" rel="noopener noreferrer">info@test.com</a>와 같이 메일 주소별로 성격을 분리해서 오는 것을 볼 수 있다.</strong></li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="메일-관리를-하면서-알게-된-것">메일 관리를 하면서 알게 된 것<a class="hash-link" href="#메일-관리를-하면서-알게-된-것" title="제목으로 바로 가기">​</a></h2><ul><li>메일 알림, 뱃지 표시와 같은 기본 기능은 받은편지함 기준으로 되어 있다.<ul><li>받은편지함 이외는 메일 알림 설정을 할 수 없는 프로그램도 있다.</li><li><strong>이처럼 메일은 애초에 받은편지함을 기준으로 사용되도록 UI가 설계되어 있다.</strong></li></ul></li><li>주로 사용할 메일 계정을 찾는다면 Naver보다는 Gmail이다.<ul><li>Gmail은 대부분의 메일 프로그램에서 거의 Full Service를 제공한다. 이걸 체감하는 부분은 구글 계정만 연동하면 캘린더, <strong>주소록이 동기화되어 메일 주소 자동완성 기능을 제공한다!</strong></li><li>모던한 웹 환경, 높은 점유율, 보관 기능은 덤이다.</li></ul></li><li>POP3, IMAP 연결은 기능이 제약이 있다.<ul><li>계정 연결을 지원하는 경우 캘린더, Push 알림 등 <code>Mail</code>의 <code>Full Service</code>를 대부분 제공한다.</li><li>반면에 IMAP 연결은 IMAP 서버의 공통된 기능(메일함 불러오기, 플래그 지정 등)만 제공되며 알림도 가져오기 방식으로 <strong>주기적으로 폴링하는 방식이라 많이 느리다.</strong></li></ul></li><li>Outlook 프로그램에서 받은편지함을 비우면 '오늘의 일정을 모두 마쳤습니다.'라는 문구가 표시된다. 이처럼 메일 프로그램도 Inbox Zero를 지향하고 있는 것을 느낄 수 있었다.</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>하고 싶은 말이 많았는데 글로 서술된 느낌보다는 보고서가 된 것 같아서 아쉽다.</p><p>본질적으로 전달하고 싶었던 것은 최대한 단순하고, 대충이라도 읽을 수 있는 환경을 조성하는 것이다.</p><p>직장인이라면 메일 관리는 당연한 것 아니야? 싶을 수 있지만 주변을 잘 보면 관리를 못하는 사람이 더 많은 것 같다. 어찌 보면 너무 단순하기에 알려줄 필요도 없어서 그럴 수 있겠다 싶지만 내가 느낀 메일 관리는 훨씬 어려운 범주에 속했다. 이러니 <a href="https://www.notion.com/product/mail" target="_blank" rel="noopener noreferrer">Notion Mail</a> 같은 새로운 슬로건의 서비스가 나오는 것이 아닐까?</p><p>메일 관리법을 찾은 뒤로는 메일을 보고, 보내는 것이 즐겁다. 시스템의 중요성을 느낀다.</p>]]></content>
        <category label="email" term="email"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[완벽주의의 무서움]]></title>
        <id>/2025/01/04/perfectionism-fear</id>
        <link href="https://parkgang.github.io/blog/2025/01/04/perfectionism-fear"/>
        <updated>2025-01-04T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[시작도 완료도 느린 완벽주의는 도움이 되는 성향일까?]]></summary>
        <content type="html"><![CDATA[<p>시작도 완료도 느린 완벽주의는 도움이 되는 성향일까?</p><p>현대인들은 완벽주의를 대부분 가지고 있다고 생각한다.</p><p>대체적인 예로 사진 찍는 것을 볼 수 있다. 우리는 흔히 말하는 <code>인생샷</code> 을 위해서 구도, 각도, 비율 전체적인 것을 고려하며 찍고 또 찍는다. 결과물이 마음에 들지 않으면 괜찮을 때까지 찍고 또 찍는다.</p><p>완벽주의는 <code>시작</code> 도 오래 걸리고 <code>완료</code> 도 오래 걸린다. <code>시작</code> 을 하기에는 준비가 <code>완벽</code> 하지 않아서 못하고 막상 시작하고 <code>완료</code> 를 하기에는 부족한 것들이 투성이라 끝내지 못한다.</p><p>생산성 증진을 위한 가장 빠른 해결 방법은 <code>완벽주의</code> 를 벗어나는 것이 아닐까? 빠른 <code>시작</code> , 빠른 <code>완료</code> 그것을 시스템화시켜서 반복하고 피드백을 받으며 성장하는 것이 생산성 증진이라는 생각이 요즘 든다.</p><p>하지만, 절대다수의 각 분야 최정상급 인물들은 <code>완벽주의</code> 성향이 강한 사람들도 많다. <code>긍정적</code> 인 측면을 발휘할 수 있도록 노력하면 오히려 좋은 성향일까?</p><p><code>시작</code> 하는 것도 어렵지만 <code>완료</code> 하는 것도 어렵다. 회사에서 프로젝트를 진행하다 보면 간단한 <code>이슈</code> 였지만 사소한 것들이 추가되고 겹치다 보면 본래 문제가 뭐였는지 까먹은 채 끝나지 않은 거대한 <code>이슈</code> 가 되어버린다. 이런 문제를 방지하기 위해서 <code>실행 가능 단위</code> 로 짧게 분리하고, 빠르게 완료해서 반복하는 것이 더 나은 성과를 만드는 것이라고 생각한다.</p><p>사실 누구나 느낄 수 있고, 아는 이론이라고 생각하지만 그럼에도 이러한 문제가 발생되고 고민하는 것을 보면 <code>시작</code> 과 <code>완료</code> 가 얼마나 어려운지 반증하는 내용이 아닐까?</p><p>나의 완벽주의는 어디를 향하고 있는 걸까?</p>]]></content>
        <category label="essay" term="essay"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[『퓨처 셀프』 후기]]></title>
        <id>/2024/11/24/future-self-book-review</id>
        <link href="https://parkgang.github.io/blog/2024/11/24/future-self-book-review"/>
        <updated>2024-11-24T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[나 자신의 문제를 해결하기 위해… 광명 찾으러]]></summary>
        <content type="html"><![CDATA[<p>나 자신의 문제를 해결하기 위해… 광명 찾으러</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAOAAoDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAABAYHCP/EACUQAAICAgEDAwUAAAAAAAAAAAEDAgQFESEABhIHQYEIExQxMv/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/8QAGREAAwADAAAAAAAAAAAAAAAAAAERITFB/9oADAMBAAIRAxEAPwCWdkK7axPZOQtZ5Fa638tY+8tQcYwkv+NEjkSPxr36UsllMM3I2mVqaIom2clxFfx1Ek6Gt8ce3WscP6GUqUBQlmW2aRdFrEOqw0yUeBuQIkPg9B3fpuwVm498MtYTFrJTC4Vx4wBO/EblvQ/XPUiWhnrp/9k=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="400" height="579"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/thumbnail.775c44e.400.jpg" srcset="/assets/ideal-img/thumbnail.775c44e.400.jpg 400w" width="400" height="579"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="계기">계기<a class="hash-link" href="#계기" title="제목으로 바로 가기">​</a></h2><p>책 구경 차 방문한 서점에서 우연히 <code>베스트셀러</code> 에 있는 책을 보고 조금 읽어봤는데… 이거 완전 나를 공략하는 책이잖아? 이걸 읽으면 내가 답답해하던 문제의 해답을 알 수 있지 않을까? 설레기 시작했다.</p><p>바쁜 일정이 지나고 시간이 남자 책을 구매해서 바로 읽기 시작했다.</p><p>나는 당시 나 자신의 여러 문제에 대해 질려있었고 어떻게든 해답을 찾고 싶었다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="후기">후기<a class="hash-link" href="#후기" title="제목으로 바로 가기">​</a></h2><ul><li><code>PART</code> 1, 2는 원론적인 내용이었다.<ul><li>왜 미래와 나를 연결되어야 하는지, 그 근거가 무엇인지에 대한 내용이었다.</li><li>여기서도 가슴을 울리는 내용이 많았지만 <code>PART 3</code> 에서 내 문제를 해결할 수 있는 직접적인 방법이 많이 등장하면서 기억이 잘 안 나게 되었다.</li></ul></li><li>전반적으로 <code>사례</code> , <code>인용</code> 에 대한 내용이 많았으며 결론적으로 하고 싶은 말은 <code>A4</code> 한 장에 요약할 수 있을 만큼 간단해 보였다.<ul><li>즉, 미래를 왜 연결해야 한다는 것만 인지하고 있다면 <code>PART 3</code> 만 중점으로 봐도 많이 도움이 될 것이라고 느껴졌다.</li></ul></li><li>사실 내가 다 느끼고, 알고 있던 내용인데 이걸 책을 통해 본 느낌이다.<ul><li>내가 문제를 느끼고 해결하기 위해서 이 책을 읽게 되었는데 내 문제를 해결하기 위한 내 해결 방법이 이 책에 거의 동일하게 등장하는 느낌을 받았다.</li><li>나만의 생각, 나만의 해결 방법이어서 이게 정말 최선일까? 이게 맞을까? 하는 걱정이 있었는데 오히려 책에서 장문으로 자세하게 이유와 해결 방법에 대해 알려주니 내가 생각하고 느끼고 판단한 해결 방법이 맞구나 생각과 함께 오히려 더 자신감이 생겼다.</li><li>이제 이 방법대로 우선 설천을 해서 나를 개선할 수 있다는 생각이 신남과 자신감이 생겼다.</li></ul></li><li>완벽주의자에게 강추! <code>PART 3</code> 에서 팩트 폭격을 때린다.<ul><li>근데 글쓴이도 글 쓰는데 시작이 <code>3년</code> 걸린 것을 보면 이 <code>완벽주의</code> 라는 것이 얼마나 골치 아프고 해결하기 어려운지 느낄 수 있는 대목이었다.</li></ul></li><li>가족과 일의 삶의 분리 중요성<ul><li><code>자기 계발</code> 을 하며 열심히 살다 보면 (생산성만 추구하며 살다 보면) 이렇게 사는 것이 맞나 생각이 들 때가 있다.</li><li>하루를 잘 보내면 뿌듯하고 미래가 기대가 되지만 휴일 혹은 기념일이 되거나, 잠시 <code>카톡</code> , <code>인스타</code> 같은 것을 보면 세상 편히 행복하게 사는 사람들을 보면 이게 맞나 무기력 해지는 경우가 있다.</li><li>뭔가를 얻으면 포기할 것도 수반되어야 하겠지만 이 책을 보면 글쓴이는 자신의 목표를 위해서 열심히 달려나가지만 <code>가족</code> 을 위한 시간도 반드시 분리해 놓더라</li><li>예를 들어 주 4일은 할 일에 집중 3일은 가족과 함께 보내는 시간에 집중처럼 말이다.</li><li>세상을 혼자 살아가는 것은 아니기에 할 일도 하되 <code>밸런스</code> 를 잘 잡는 것이 중요하다고 느껴졌습니다.</li></ul></li><li>보면 시작도 어렵고 완료도 어렵다고 생각된다. 빠른 시작 빠른 완료 이걸 주기적으로 하는 것이 빠른 성장의 요점이라고 생각된다.</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="-포인트-문장-">🔥 포인트 문장 🔥<a class="hash-link" href="#-포인트-문장-" title="제목으로 바로 가기">​</a></h2><ul><li><code>파킨슨의 법칙</code> : 목표를 당겨라. 그럼 그에 맞게 빨리 갈 수 있는 길을 생각한다.<ul><li>저는 어떤 목표를 생각할 때 물리적으로 들어가야 하는 시간이 너무 많아서 막막하고, 언제 따라 잡지? 이런 생각을 하곤 했습니다.</li><li>그런데 책에서는 이런 것을 목표를 잡고 기간을 당기라고 하더군요?</li><li>그럼 그 기간에 맞게 목표를 달성하기 위해서 버릴 것들, 지름길을 생각하게 된다고 헸는데… 생각해 보니 맞는 말이었습니다.</li><li>어떤 목표를 달성하기 위해서 거쳐야 하는 일들은 말로 표현하기 어려울 정도로 많습니다.</li><li>하지만 <code>시간</code> 은 한정되어 있고 상황에 따라 <code>깊이</code> 를 다르게 가져가야 할 것입니다.</li><li>꼭 다 할 필요는 없다는 것이죠.</li><li>예로<ul><li>여행을 위해 항공권을 예약하는데 모든 사이트를 비교하는 것이 아니라 TOP3만 찾아서 비교하는 것입니다. 더 비쌀 수 있죠 하지만 시간은 없습니다.</li><li>사진으로 찍은 내용을 용량을 줄이기 위해서 사진이 아닌 텍스트로 뽑고 싶지만 시간이 없습니다. 그냥 사진으로 첨부합니다.</li><li>책 읽은 독후감 1시간 동안 쓰라고 하는 깊이와 30분 동안 쓰라고 하는 깊이를 비교하면 당연히 1시간이 더 길겠죠? 하지만 30분 만에 핵심만 적은 것이 대충 쓰고 잘못된 것일까요? 30분이라는 목표를 잡았으면 저는 독후감도 다 쓰고 다른 30분을 다른 곳에 사용할 수 있습니다.</li><li>더 적으려고 하니까 생각이 안 나네요… 뭐가 되었던 목표를 위해 버릴 수 있는, 타협할 수 있는 작업들은 분명히 있다는 것입니다. 기간이 길면 줄일 생각하지 않고 다 챙기려고 하겠죠. 하지만 일정을 줄이면 그 일정 안에 달성을 위한 작업을 줄이고 줄일 것입니다.</li></ul></li><li>이는 중요한 가치라고 느껴졌습니다. 한정된 시간 안에서 많은 목표를 이루기 위한 전략인 것이죠</li></ul></li><li><code>80%</code> 법칙<ul><li>100% 하려면 끝이 없다 80% 할 때 결과가 나온다.</li></ul></li><li><code>전념</code> 해라<ul><li>여러 일을 하려고 하지 말아라</li><li>한 가지 일에 전념하고 집중해라</li><li>그렇게 반복적으로 완료해라</li></ul></li><li><code>시급한 일</code> 보다 <code>중요한 일</code> 을 먼저 해라</li><li>미래의 목표를 크게 잡아라<ul><li>평범한 결혼 생활이든 행복한 결혼 생활이든 결혼 생활을 유지하는 데는 똑같은 양의 에너지가 들어간다.</li><li>어차피 똑같은 에너지 더 큰 목표를 잡아서 쟁취해라</li></ul></li><li>결정의 기본은 <code>기회비용</code> 을 기꺼이 받아들이는 것이다.</li><li><code>미래의 나</code> 를 <code>현재</code> 의 나와 <code>다른 사람</code> 으로 보는 게 대단히 중요하다.</li><li>원하는 것이 이미 당신 것이라는 사실을 알고 받아들여라. 그러면 당신의 목표는 <code>종착지</code> 가 아니라 <code>출발선</code> 이 된다.<ul><li>여행을 예시로 들어보자</li><li>어차피 나는 여행을 간다.</li><li>귀찮고, 완벽하게 찾으려고 미루지 말아라 나는 이미 여행을 가고, 출발을 시작했다.</li></ul></li><li>진정한 꿈과 상관없는 일을 처리해 줄 수 있는 사람은 어디에서 찾을 수 있을까?</li><li>실패에 투자해라<ul><li>자동성이 나타난다면 그동안 익힌 것들이 그 수준에 머물러 있게 되고, 시간이 지나며 점점 퇴보한다.</li><li>20년 경력이 마치 1년 경력을 20번 반복한 것처럼 돼버린다.</li></ul></li><li><code>효과성</code> 은 적절한 일을 하는 것이고, <code>호율성</code> 은 일을 적절하게 하는 것이다. <code>효과성</code> 은 언제나 먼저고, 그 다음이 <code>효율성</code> 이다.</li><li>승리하는 사람은 언제나 <code>포기</code> 한다. 그들은 적절한 시점에 적절한 일을 포기할 줄 안다.</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마치며">마치며<a class="hash-link" href="#마치며" title="제목으로 바로 가기">​</a></h2><ul><li>계속 우선순위에 밀려 읽을 일이 있을까 싶었지만 살다 보니 나의 완벽주의로 인한 영원히 끝나지 않는 문제를 너무나도 많이 겪으면서 책을 읽어서라도 내가 바뀌지 않으면 평생 이렇게 살겠구나 라는 경각심으로 다 읽게 되었네요…</li><li>이 책을 읽음으로써 책의 인용된 내용을 상기하며 제 인생이 조금씩 바뀌고 있다는 것을 느끼었습니다.</li><li>책을 읽는다는 것이 공부 시간을 뺏긴다고 생각했는데 <code>공부</code> 와는 완전히 다른 의미이었습니다.<ul><li>공부는 한 장 한 장 집중해서 머리에 이해하는 과정이 필요한데 이러한 책들은 아하! 하고 넘어가고 상기할 정도의 레벨이었습니다.</li><li>쉬는 시간에 짬짬이 보는 용도인 것이죠.</li><li>차란히 유튜브 보는 것보다 훨씬 좋습니다. 종이책이 물리적으로 있으면 손이 자주 가기도 하고요.</li></ul></li><li>책을 다 읽었다는 뿌듯함도 있고 한번 경험을 하니 앞으로 주기적으로 어떻게 읽어야 할지 감도 생기고 있습니다.</li><li>위대한 첫 출발이라고 생각합니다. 저의 문제를 해결하면 저는 더 높이 올라갈 수 있다고 싶습니다.</li><li>이번 기회로 앞으로 재미있는 책을 더 많이 읽어봅시다!</li><li>참 그리고 이 책의 <code>PART 3</code> 만 따로 다시 읽어봐도 좋을 거 같습니다.</li></ul>]]></content>
        <category label="책 후기" term="책 후기"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[웹 개발자가 바라본 AI 개발이란?]]></title>
        <id>/2024/08/01/web-dev-view-ai-dev</id>
        <link href="https://parkgang.github.io/blog/2024/08/01/web-dev-view-ai-dev"/>
        <updated>2024-08-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[AI 개발이 핫하다. AI 개발이 어떻게 돌아가는지는 알아야 새로운 소식을 듣고 쫓아갈 텐데... 웹 개발자 시선으로 AI 개발에 대해 정리해보자.]]></summary>
        <content type="html"><![CDATA[<p><code>AI</code> 개발이 핫하다. <code>AI</code> 개발이 어떻게 돌아가는지는 알아야 새로운 소식을 듣고 쫓아갈 텐데... 웹 개발자 시선으로 <code>AI</code> 개발에 대해 정리해보자.</p><p>요즘 여러 <code>LLM</code> 이 출시되면서 <code>AI</code> 개발이 궁금해졌다.</p><p>트렌드에 쫓아가야 하는데 한 번도 경험하지 않은 개발이다 보니 뉴스레터를 통해 접해도 잘 이해되지 않는다.</p><p>이에 대해 기초적인 내용을 정리해보자. 1:1 매칭은 안되겠지만 웹 개발자 시선으로 맵핑해서 다른 개발자도 이해하기 쉽도록 정리해보자.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="개념-정리">개념 정리<a class="hash-link" href="#개념-정리" title="제목으로 바로 가기">​</a></h2><div class="admonition admonition-caution alert alert--warning"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>caution</h5></div><div class="admonition-content"><p>얕게 살펴본 개념이며 틀릴 수 있습니다.</p></div></div><ul><li>Model<ul><li>라이브러리? 같은 것으로 볼 수 있음 왜냐면 어디든지 붙여서 사용할 수 있으니까</li><li>하나의 구현체임</li><li>여러 Model이 있지만 가장 각광 받고 있는 형태는 LLM 임</li><li>LLM은 여러 언어를 학습한 것이기 때문에 AI로 뭔가 만들 때 챗봇 이런 언어적인 것들이 많이 등장하는 것이었음</li></ul></li><li>Hugging Face (허깅 페이스)<ul><li>Model 올리고 공유하는 곳인데 하나의 인스턴스가 올라간다는 점에서 <code>Docker Hub</code>와 유사하다고 볼 수 있음</li></ul></li><li>LangChain (랭체인)<ul><li>프레임워크? 같은 것으로 볼 수 있음</li><li>더 나아가면 interface라고도 볼 수 있을 듯</li><li>왜냐면 여러 도구 체인을 묶어서 하나로 연결해주기 때문임</li><li>각광받는 LLM에 여러 정보를 넣고 싶을 때 해당 도구를 사용하면 손쉽게 할 수 있어서 각광 받는 중</li></ul></li><li>Ollama (올라마)<ul><li>플랫폼, 런타임 환경이라고 생각할 수 있음</li><li>모델을 로컬에서 실행할 수 있도록 만들어줌</li></ul></li><li>메타의 라마(LLaMA) 모델<ul><li>여러 LLM 모델 중 성능이 좋고 오픈된 것이라 인기가 많음</li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="요약">요약<a class="hash-link" href="#요약" title="제목으로 바로 가기">​</a></h2><p>정말 단순하게 AI 개발이라고 하면</p><ul><li>처리할 데이터를 정하고</li><li>모델을 만들 수 있으면 모델을 만드는 것이고</li><li>모델을 사용한다면 모델이 잘 처리할 수 있도록 데이터 전처리 과정을 거쳐서 여러 모델과 랭체인으로 연결하여 하나의 목표(비즈니스)를 위한 AI를 만든다라고 볼 수 있다.</li></ul><p><code>AI</code> 를 사람이라고 생각한다면 그 사람에게 어떤 일을 시킬지 디테일하게 조정하는 작업이라고도 볼 수 있다.</p><p>사실 더 심오하게 들어가면 모델 만드는 방법, 파인튜닝 등 많지만 아주 얕게 살펴보았다.</p>]]></content>
        <category label="AI" term="AI"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Docker에서 systemctl 사용 방법]]></title>
        <id>/2024/04/28/docker-systemctl-workaround</id>
        <link href="https://parkgang.github.io/blog/2024/04/28/docker-systemctl-workaround"/>
        <updated>2024-04-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Docker에서 systemctl 을 사용할 일이 생겼다. 당연하게 될 줄 알았는데 사용하는데 애 좀 먹었다. 그 과정을 정리해 본다.]]></summary>
        <content type="html"><![CDATA[<p>Docker에서 <code>systemctl</code> 을 사용할 일이 생겼다. 당연하게 될 줄 알았는데 사용하는데 애 좀 먹었다. 그 과정을 정리해 본다.</p><p>당시 <code>CentOS 7</code> 를 사용하고 있었고 <code>Docker</code> 는 <code>macOS</code> 에서 실행하고 있었다.</p><p>해당 환경에서 오류가 발생하는데 찾고 찾아도 정말 답이 없어서 <code>CentOS 8</code> 으로 써야 하나 했다... <code>CentOS 8</code> 은 <code>privileged</code> 값 같은 것 설정하지 않아도 그냥 잘 되었던 것으로 알고 있다.</p><p>Docker에서 <code>systemctl</code> 가 안 되는 것은 <code>CentOS</code> 에 국한된 것이 아니라 <code>Docker</code> 컨테이너를 띄우면 발생할 수 있는 문제이었다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="결론-사용-방법">결론: 사용 방법<a class="hash-link" href="#결론-사용-방법" title="제목으로 바로 가기">​</a></h2><ul><li>Windows에서는 문제가 없었다.</li><li>macOS의 경우 <code>~/Library/Group\ Containers/group.com.docker/settings.json</code> 의 <code>deprecatedCgroupv1</code> 값을 <code>true</code> 으로 변경하고 <code>Docker Desktop</code> 완전 종료 후 실행해야 한다.(재시작 X)</li><li>Mac의 M 시리즈도 잘 된다. (테스트해봄)</li><li>해당 이슈와 관련하여 찾아보면 많이 등장하는 <code>cgroup</code> 관련 볼륨 마운트 할 필요 없고 <code>/usr/sbin/init</code> , <code>privileged</code> 지정하고 <code>exec</code> 으로 <code>bash</code> 으로 접근해서 사용하면 된다. (애초에 호스트가 리눅스가 아니라 상관 없다.)</li><li>찾아보면 다들 <code>cgroup</code> 볼륨 마운트 예시만 있는데 나는 <code>mac</code> 이라서 관련 디렉터리가 없는데 그래서 안되는 건가 한참 헤맸지만 그런 것은 아니었다.</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="문제">문제<a class="hash-link" href="#문제" title="제목으로 바로 가기">​</a></h2><ul><li><p><code>CentOS</code> <code>apachectl</code> 와 같은 시스템 명령이 동작하지 않았습니다.</p></li><li><p><code>"Failed to get D-Bus connection: Operation not permitted"</code> 오류가 발생합니다.</p></li><li><p>컨테이너를 실행할 때 <code>--privileged</code> 옵션을 추가하여 컨테이너에 더 많은 권한을 부여할 수 있다고 하는데 해당 옵션을 주더라도 해결되지 않았습니다.</p></li><li><p>아래의 명령으로 잘 동작하는지 테스트 했습니다.</p><div class="language-bash codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-bash codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> yum </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> at</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> systemctl </span><span class="token builtin class-name">enable</span><span class="token plain"> atd</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 해당 부분에서 에러 발생</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> systemctl start atd</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> systemctl status atd</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">echo</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"sleep 5; echo 'Hello World' &gt; /tmp/test.txt"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> at now</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="트러블슈팅">트러블슈팅<a class="hash-link" href="#트러블슈팅" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="오류-메시지-기반으로-검색">오류 메시지 기반으로 검색<a class="hash-link" href="#오류-메시지-기반으로-검색" title="제목으로 바로 가기">​</a></h3><p>당시 <code>systemctl</code> 입력 당시 나오는 오류 메시지로 계속 검색하면서 나온 글을 스크랩한 것입니다.</p><p>대부분 글에서 하는 내용은 <code>privileged</code> 를 활성화하고 <code>/usr/sbin/init</code> 으로 실행해라 이었습니다.</p><ul><li><code>mac</code> 에서 추가적으로 설정하라는 값 알려주는 글 (이 글이 맞았습니다)<ul><li><a href="https://bill1224.tistory.com/435" target="_blank" rel="noopener noreferrer">[ Docker ] systemctl사용시 Failed to get D-Bus connection 에러 해결</a></li></ul></li><li>M 시리즈 맥은 어차피 <code>CentOS 7</code> 안된다는 글 (아닙니다 테스트 해보니까 잘 됩니다)<ul><li><a href="https://jakpentest.tistory.com/entry/DockerCentOS%EB%A1%9C-systemctl-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0" target="_blank" rel="noopener noreferrer">[Docker]CentOS로 systemctl 사용하기</a></li></ul></li><li>다들 블로그 글에 있는 건데 자세한 설명<ul><li><a href="https://naruu098.tistory.com/64" target="_blank" rel="noopener noreferrer">docker centos, ubuntu systemctl(service)</a></li></ul></li><li>복잡하고 <code>Dockerfile</code> 직접 작성해서 수행하는 글<ul><li><a href="https://scbyun.com/entry/CentOS-7-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%82%B4%EC%97%90%EC%84%9C-systemctl%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95centos-init" target="_blank" rel="noopener noreferrer">CentOS 7 컨테이너 내에서 systemctl을 사용하는 방법(centos init)</a></li></ul></li></ul><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="macos에서-deprecatedcgroupv1-값-false-처리-이후-종료-후-재시작-시도">macOS에서 <code>deprecatedCgroupv1</code> 값 <code>false</code> 처리 이후 종료 후 재시작 시도<a class="hash-link" href="#macos에서-deprecatedcgroupv1-값-false-처리-이후-종료-후-재시작-시도" title="제목으로 바로 가기">​</a></h3><p><a href="https://mosei.tistory.com/entry/Docker-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%97%90%EC%84%9C-systemctl-%EC%82%AC%EC%9A%A9-%EB%AC%B8%EC%A0%9C" target="_blank" rel="noopener noreferrer">[Docker] 컨테이너에서 systemctl 사용 문제</a> 글을 보고 뭔가 <code>재시작</code> 이 아닌 종료 후 시작에 대한 힌트를 얻었다.</p><p>왜냐면 위에서 여러 글을 보면서 테스트 하던 중 한번은 <code>systemctl</code> 이 되었던 적이 있었기 때문이다. 그리고 그 과정에서 <code>OS 재부팅</code> 을 한 적이 있는데 그러면서 자연스럽게 <code>Docker</code> 가 다시 시작되고 문제가 해결된 것이라고 생각했다.</p><p><code>deprecatedCgroupv1</code> 값 변경 후 <code>Docker</code> 를 종료 후 재시작하니 잘 되는 것을 보았고 트리거 조건을 찾기 시작했다.</p><p>가정은 아래와 같다.</p><ul><li>OS 재부팅 때문?</li><li>Docker 설정때문?</li><li>Docker 종료 후 재시작 때문?</li></ul><p>가정 테스트</p><ul><li><code>deprecatedCgroupv1</code> 값을 <code>false</code> 로 바꾸고<ul><li>docker-compose <code>down</code> 후 다시 <code>up</code> : 잘됨</li><li>docker <code>재시작</code> : 잘됨</li><li>docker <code>재시작</code> 후 <code>down</code> 후 다시 <code>up</code> : 잘됨</li><li>docker <code>종료</code> 후 <code>재시작</code> : 안됨</li></ul></li><li><code>deprecatedCgroupv1</code> 값을 <code>true</code> 로 바꾸고<ul><li>docker <code>종료</code> 후 <code>재시작</code> : 잘됨</li></ul></li><li>docker 공장 초기화 후 <code>deprecatedCgroupv1</code> 값 바꾸면?<ul><li>공장 초기화 후 <code>~/Library/Group\ Containers/group.com.docker/settings.json</code> 의 <code>deprecatedCgroupv1</code> 값이 <code>false</code> 으로 변경되는 것을 봄</li><li>값을 <code>true</code> 로 바꾸고 종료 후 시작하니까 잘됨</li></ul></li></ul><p>다른 플랫폼 테스트</p><ul><li>애플 실리콘<ul><li><code>M1 MacBook Air</code> , <code>M2 MacBook Air</code> 으로 잘 되는 것을 확인함</li></ul></li><li>Windows<ul><li><code>deprecatedCgroupv1</code> 값을 변경하지 않아도 잘됨</li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>Docker에서 당연히 되는 기능이라고 생각했는데 안되어서 당황했다.</p><p>대부분의 글에서 <code>cgroup</code> 관련 볼륨 마운트 예시만 나오고 나는 <code>mac</code> 이라서 관련 디렉터리가 없는데 그래서 안되는 건가 한참 헤매고, Docker Desktop의 settings.json 을 변경 후 재시작이 아닌 종료 후 시작해야 한다는 것을 몰라서 한참 또 헤맸다.</p><p>결론을 보면 별것이 아닌데 한참 헤맸다. 누군가 이 글을 보고 헤매는 시간을 줄였으면 좋겠다.</p>]]></content>
        <category label="Docker" term="Docker"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[WSL에서 Windows에 설치된 Docker 명령어를 사용할 수 있는 이유]]></title>
        <id>/2024/04/27/use-windows-docker-in-wsl</id>
        <link href="https://parkgang.github.io/blog/2024/04/27/use-windows-docker-in-wsl"/>
        <updated>2024-04-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Docker 설치는 Windows 에 했는데 PowerShell 에서도 WSL 에서도 docker , docker-compose 명령이 가능한데 이유가 뭘까?]]></summary>
        <content type="html"><![CDATA[<p><code>Docker</code> 설치는 <code>Windows</code> 에 했는데 <code>PowerShell</code> 에서도 <code>WSL</code> 에서도 <code>docker</code> , <code>docker-compose</code> 명령이 가능한데 이유가 뭘까?</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="가능한-이유">가능한 이유<a class="hash-link" href="#가능한-이유" title="제목으로 바로 가기">​</a></h2><p><code>Docker Desktop</code> 앱 자체에서도 <code>가상 디스크</code> 를 만들어서 공유하기 때문이다. 관련 명령어들이 <code>WLS</code> 에 <code>가상 디스크</code> 로 <code>mount</code> 되어있는 것을 볼 수 있다.</p><ul><li><code>cd /mnt</code> 해서 <code>ll</code> 하니까 <code>Windows</code> 의 <code>C 드라이브</code> 와 마운트된 <code>c</code> 도 보이는데 <code>wsl</code> 과 <code>wslg</code> 은 뭔가 하고 찾아보았다.<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAV0lEQVQImQXByxFAMBRAUX0wFsxIQj5vRjI+WdGAQjRgZ2mjAdVe5xTn/bJfHzZOaO3QxtMZR6sGGmVpe0/nA0XKmXXJBBmxTkgxouZEKYFKhHqbic/BD1ILIOkua7m2AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="682" height="132"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.3f54b5c.682.png" srcset="/assets/ideal-img/1.3f54b5c.682.png 682w" width="682" height="132"></noscript></div></div></li><li>가상 디스크 조회하면서 보니까 <code>docker-desktop</code> 이 <code>~\AppData\Local\Docker\wsl\distro\ext4.vhdx</code> 경로로 사용하던데 그것이 아닐까 생각이 들었다.<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAVUlEQVQImR3D3Q1AMBSAUZs0Elcj1ANp6ycqUg9eLGAGJjCCoT/hJCc5rptwPpi4I1KgdU2uzT/LDfKVkmSMG37dsUOgmTpSZ1G+RfWW0s0YtyCV4wVLRSC3J7JVcAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="742" height="150"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.4f9c9e1.742.png" srcset="/assets/ideal-img/2.4f9c9e1.742.png 742w" width="742" height="150"></noscript></div></div></li><li>조회해보니까 다른 건 모르겠고 알만한 <code>cli</code> 도구를 가보니까 <code>docker-compose</code> 가 있었다.<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAV0lEQVQImQXBywpAQABAUd9Cnoki5dEwhkz8gY3VJHtlNfz6dY6zXzfy/FDnizKWwVh6Y6mPh6jqCOKUIMlwhJS0w0I3r4yTJtQb3rjgKo0rGiJR4Jc5PxCIJDgsPLdaAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="704" height="190"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.265b205.704.png" srcset="/assets/ideal-img/3.265b205.704.png 704w" width="704" height="190"></noscript></div></div></li><li>그렇지만 <code>which</code> 으로 <code>docker-compose</code> 를 조회하면 <code>WSL</code> 경로로 나오는데<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAMklEQVQImWOIyiz775rT/t81q/m/TVLNf/PY8v+W8VX/TSOL/qvo6v/XNzD5b2Bo9h8An4oTeyAPWv8AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="723" height="53"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.6241cec.723.png" srcset="/assets/ideal-img/4.6241cec.723.png 723w" width="723" height="53"></noscript></div></div></li><li>혹시 심볼릭 링크로 연결된 것이 아닐까? 하고 보니까 그렇더라<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAALUlEQVQImWNwD03+b2xm+d/I0vq/vJbef1Vto/9qOiBs/F9d1+S/OojWM/0PAEL+D9iflGxTAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1413" height="53"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.1519111.1413.png" srcset="/assets/ideal-img/5.1519111.1413.png 1413w" width="1413" height="53"></noscript></div></div></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="결론">결론<a class="hash-link" href="#결론" title="제목으로 바로 가기">​</a></h2><p><code>Windows</code> 에서 <code>Docker</code> 를 설치하면 관련 명령과 정보들이 <code>가상 디스크</code> 로 <code>WSL</code> 에 마운트 되어서 사용되고 있던 것입니다.</p>]]></content>
        <category label="Docker" term="Docker"/>
        <category label="WSL" term="WSL"/>
        <category label="Windows" term="Windows"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Windows Docker는 WSL 배포판을 따로 설치하지 않아도 잘 동작한다.]]></title>
        <id>/2024/04/26/windows-docker-without-wsl-dist</id>
        <link href="https://parkgang.github.io/blog/2024/04/26/windows-docker-without-wsl-dist"/>
        <updated>2024-04-26T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[늘 Windows 에서 Docker 를 설치할 때 WSL 배포판 하나 이상을 설치해놓고 시작해야 하는 것으로 알고 있었는데 그렇지 않다는 것을 알게 되어 정리합니다.]]></summary>
        <content type="html"><![CDATA[<p>늘 <code>Windows</code> 에서 <code>Docker</code> 를 설치할 때 <code>WSL</code> 배포판 하나 이상을 설치해놓고 시작해야 하는 것으로 알고 있었는데 그렇지 않다는 것을 알게 되어 정리합니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="과정">과정<a class="hash-link" href="#과정" title="제목으로 바로 가기">​</a></h2><ul><li><p><code>wsl</code> 명령어로 아무것도 안하고 <code>Windows</code> 설치하자마자 <code>Docker Desktop</code> 설치하니까 알아서 <code>docker-desktop</code> , <code>docker-desktop-data</code> 설치되어있음 이 상태로도 잘 동작함</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAABYlAAAWJQFJUiTwAAAASklEQVQImR3KQQ6AIAwAQR4AnDDhUjHUUknA//9uTZzzBL0N1U5XZc4Hs4G7c4rQe0NEqLUS1n7xYX/Ye2FmXK1xlEKMiZQSOWc+ZMAbUfMO0aUAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="530" height="132"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.4285586.530.png" srcset="/assets/ideal-img/1.4285586.530.png 530w" width="530" height="132"></noscript></div></div></li><li><p>이 상태에서 <code>Ubuntu</code> 배포판으로 접속해서 <code>docker ps</code> 입력하면 없다고 나옴</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAZElEQVQImR3IvQqCIRhAYftw0DdCBXWQBG+0oSF/aPCST+TwwOGo7/vFpw/WnOy9WWsxxmDOefy79456OI9zDmstIoK1gsgdY8x5Wmuu24WqtXI8K601SinknEkpEWPEe08IgR/w7DGu1ZO+XAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1152" height="324"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.45128a1.1152.png" srcset="/assets/ideal-img/2.45128a1.1152.png 1152w" width="1152" height="324"></noscript></div></div><div class="codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-text codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">The command 'docker' could not be found in this WSL 2 distro.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">We recommend to activate the WSL integration in Docker Desktop settings.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">For details about using Docker Desktop with WSL 2, visit:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">https://docs.docker.com/go/wsl2/</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p><code>wsl -d docker-desktop</code> 으로 들어가도 없다고 나오고</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAcElEQVQImQXB0Q6CIABAUd9z1ZMYYrO/FLAYtj6yN+caoPQLdDunWvKP9/IlfFb2PRNCYNs2cs7EGEkpUUqhevgXzj/x3mO0RhvDfZqw1uKcYxxH5nmmkrJDdRIhBLdhoGkE59ORtr2glKLvr9T1gT/UhUrQGbFxHAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="404" height="126"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.f03fbc2.404.png" srcset="/assets/ideal-img/3.f03fbc2.404.png 404w" width="404" height="126"></noscript></div></div></li><li><p><code>wsl -d docker-desktop-data</code> 으로 들어가면 에러가 발생</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAWklEQVQImTWMOwoDIBAF7f0UimCRVTei6P0POEEh1YM3w5g5J3tvzjlvxxioKq01Ukp477HWYq641nrSVxUR4SNC751aK6UUYoyYC+5xC3+QcyaE8ErOuVf9AaDZJ47W3qqaAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1944" height="546"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.8a3b035.1944.png" srcset="/assets/ideal-img/4.8a3b035.1944.png 1944w" width="1944" height="546"></noscript></div></div></li><li><p><code>wsl --set-default Ubuntu</code> 으로 기본 배포판 지정하니까 잘 나옴</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAABYlAAAWJQFJUiTwAAAALklEQVQImWPQ19f/r66u/l9BQeG/srLyfzk5uf8SEhL/RUVF/4uJif0XEREB8wEQGA1p2XjTXAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1124" height="66"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.9fef253.1124.png" srcset="/assets/ideal-img/5.9fef253.1124.png 1124w" width="1124" height="66"></noscript></div></div></li><li><p>그렇다는 건 <code>기본 배포판</code> 에서는 <code>Docker</code> 명령이 가능하다는 것인데 <code>wsl --install -d Debian</code> 으로 설치한 다른 배포판도 같은 논리로 동작하며 그렇게 되면 <code>Debian</code> 배포판에서는 잘 조회되고 <code>Ubuntu</code> 배포판에서는 조회가 되지 않는다.</p></li><li><p><code>Ubuntu</code> 와 같은 배포판이 없더라도 <code>Windows</code> 에서 <code>Docker Desktop</code> 은 잘 동작한다.</p></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="결론">결론<a class="hash-link" href="#결론" title="제목으로 바로 가기">​</a></h2><ul><li><p><code>Docker Desktop for Windows</code> 는 기본적으로 <code>WSL2</code> 사용을 하도록 되어있는데 이는 <code>WSL</code> 의 특정 배포판을 선택해서 돌아가는 것이 아니라 <code>Docker</code> 가 알아서 <code>배포판</code> 을 만들어서 설치한다.</p><ul><li><p>공식문서에 보면 어디서 <code>Docker</code> 가 실행되는지 잘 나와있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA2ElEQVQImQHNADL/AB0kKv8uNTr/xsfI/+vr6//l5eX/5ubm//b29f+6vcD/Ex4o/xwiKP8AGB8g/ykwMf/Hycn/8/P0/+7u7//t7e7//f3+/8LExP8WHh//HiUm/wAZKED/ITFO/8vM0P/v7en/6+rp/+fm5P/7+fX/wcTM/woZOP8QHjn/ABM7mf8TOpv/sbzU/9/k6//c4ej/2eDr/+Xq7v+uu9T/Djqi/wYxmv8AJEyc/z9hoP8+YaH/SXC1/0lwtf9Kd8r/S3nL/zdgtP8pUqz/ETqb/93zhhg43/G0AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1542" height="874"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.3b1079c.1542.png" srcset="/assets/ideal-img/6.3b1079c.1542.png 1542w" width="1542" height="874"></noscript></div></div><blockquote><p><a href="https://docs.docker.com/desktop/wsl/" target="_blank" rel="noopener noreferrer">https://docs.docker.com/desktop/wsl/</a></p></blockquote></li></ul></li><li><p>추가적으로 설치한 <code>WSL</code> 배포판에서도 <code>docker</code> 를 사용하고 싶다면 <code>WSL 배포판</code> 을 기본 값으로 설정하면 된다.</p></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="reference">Reference<a class="hash-link" href="#reference" title="제목으로 바로 가기">​</a></h2><ul><li>공식문서: <a href="https://docs.docker.com/desktop/wsl/" target="_blank" rel="noopener noreferrer">Docker Desktop WSL 2 backend on Windows</a></li></ul>]]></content>
        <category label="Docker" term="Docker"/>
        <category label="WSL" term="WSL"/>
        <category label="Windows" term="Windows"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[표준 스트림: stdout만 보는 grep, stderr는 어떻게?]]></title>
        <id>/2024/04/01/stdout-grep-stderr</id>
        <link href="https://parkgang.github.io/blog/2024/04/01/stdout-grep-stderr"/>
        <updated>2024-04-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[grep 에서 stderr 는 필터링되지 않는다고 한다. 그럼 리다이렉션 시켜야 하는데 잠깐만... 표준 입출력이라면 각 프로그래밍 언어에서 stdin , stdout , stderr 는 어떻게 매칭될까? 에러는 정말 stderr 로 출력되고 있을까?]]></summary>
        <content type="html"><![CDATA[<p><code>grep</code> 에서 <code>stderr</code> 는 필터링되지 않는다고 한다. 그럼 리다이렉션 시켜야 하는데 잠깐만... 표준 입출력이라면 각 프로그래밍 언어에서 <code>stdin</code> , <code>stdout</code> , <code>stderr</code> 는 어떻게 매칭될까? 에러는 정말 <code>stderr</code> 로 출력되고 있을까?</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="개요">개요<a class="hash-link" href="#개요" title="제목으로 바로 가기">​</a></h2><p><code>cp -f</code> 를 하는데 같은 파일이 이미 있다고 경고 메시지가 나왔다. 문제가 될 수 있지만 <code>-f</code> 로 강제화했기 때문에 큰 관심이 없었다. 하지만 이것이 계속 표시되니 실질적인 오류와 구별이 되지 않아 불편했다.</p><p>경고 메시지를 제외하기 위해 <code>grep</code> 으로 필터링하려 했지만 제대로 되지 않았다. 이유는 <code>grep</code> 은 <code>표준 출력(stdout)</code> 만 필터링하는데 <code>cp</code> 명령어의 경고 메시지는 <code>표준 에러(stderr)</code> 로 출력되기 때문이다. 이를 해결하기 위해선 <code>stderr</code>를 <code>stdout</code> 으로 리다이렉션해야 한다고 한다.</p><p><code>stdin</code> , <code>stdout</code> 는 <code>C언어</code> 를 하며 보았던 익숙한 친구인데 이런 상황에서 만나니 어떤 역할을 하는지 까먹었다. 어떤 프로그램이든 바이너리로 컴파일되면 <code>표준 스트림</code> 형식으로 처리된다는 것인데, 각 언어마다 어떻게 구현되는지 궁금증이 생겼다. 또한, 이를 잘 활용하면 <code>CLI</code> 프로그램 만들 때 의미론적인 의도로 정보를 전달할 수 있어 보였다.</p><p>한번 알아보자.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="리다이렉션redirection">리다이렉션(Redirection)<a class="hash-link" href="#리다이렉션redirection" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="방법">방법<a class="hash-link" href="#방법" title="제목으로 바로 가기">​</a></h3><p>먼저, 이 궁금증이 시작되었던 <code>grep</code> 에서 <code>표준 에러(stderr)</code> 를 필터링하기 위해선 <code>표준 출력(stdout)</code> 으로 리다이렉션해야 하는데, 리다이렉션의 주요 내용은 아래와 같다.</p><table><thead><tr><th>파일디스크립터(일련번호)</th><th>이름</th><th>용도</th></tr></thead><tbody><tr><td>0</td><td>표준 입력(stdin)</td><td>명령어에 입력될 내용을 저장</td></tr><tr><td>1</td><td>표준 출력(stdout)</td><td>명령어에서 출력될 내용을 저장</td></tr><tr><td>2</td><td>표준 오류(stderr)</td><td>명령어에서 출력될 에러 메시지를 저장</td></tr></tbody></table><ul><li>Redirection 종류<ul><li><code>&lt;</code></li><li><code>&gt;</code> (덮어쓰기)</li><li><code>&gt;&gt;</code> (파일 끝에 추가)</li><li><code>|</code></li></ul></li><li>기타<ul><li><code>&gt;</code> 는 <code>1&gt;</code> 의 축약된 형태</li><li>어떤 사유로든 출력을 아예 무시하고 싶다면 <code>/dev/null</code> 로 지정</li></ul></li></ul><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="예제">예제<a class="hash-link" href="#예제" title="제목으로 바로 가기">​</a></h3><ul><li><p>출력 리다이렉션: <code>&gt;</code></p><div class="language-bash codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-bash codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">cat</span><span class="token plain"> .gitignore </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> test.log</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">cat</span><span class="token plain"> test.log</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">.idea</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">.DS_Store</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>오류 리다이렉션: <code>2&gt;</code></p><div class="language-bash codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-bash codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">cat</span><span class="token plain"> gitignore </span><span class="token operator file-descriptor important" style="color:#393A34">2</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> test.log</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">cat</span><span class="token plain"> test.log</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cat: gitignore: No such </span><span class="token function" style="color:#d73a49">file</span><span class="token plain"> or directory</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>출력과 오류 모두 출력: <code>&amp;</code></p><div class="language-bash codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-bash codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">cat</span><span class="token plain"> .gitignore </span><span class="token operator" style="color:#393A34">&amp;&gt;&gt;</span><span class="token plain"> test.log</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>출력 무시</p><div class="language-bash codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-bash codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 성공 출력</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">cat</span><span class="token plain"> gitignore </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> /dev/null</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 오류 출력</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">cat</span><span class="token plain"> gitignore </span><span class="token operator file-descriptor important" style="color:#393A34">2</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> /dev/null</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>출력과 오류 분리하여 로그 저장</p><div class="language-bash codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-bash codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">명령어</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> success.log </span><span class="token operator file-descriptor important" style="color:#393A34">2</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> error.log</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>오류 출력을 표준 출력으로 결합: <code>2&gt;&amp;1</code></p><div class="language-bash codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-bash codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">명령어</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> any.log </span><span class="token operator file-descriptor important" style="color:#393A34">2</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token file-descriptor important">&amp;1</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li></ul><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="주의점">주의점<a class="hash-link" href="#주의점" title="제목으로 바로 가기">​</a></h3><ul><li>왜 <code>2&gt;&amp;1</code> 에서 <code>&amp;</code> 기호를 붙이는가<ul><li><code>&amp;</code> 를 안 쓰면 파일로 읽히기 때문에 구분 값으로 사용됨</li></ul></li><li>파일디스크립터 지정할 때 <code>&gt;</code> 와 붙여야 한다.<ul><li><code>2 &gt; /dev/null</code> 안 되고 <code>2&gt; /dev/null</code> 해야 함</li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="파이프-처리를-쪼개보면">파이프 처리를 쪼개보면<a class="hash-link" href="#파이프-처리를-쪼개보면" title="제목으로 바로 가기">​</a></h2><p>리다이렉션(Redirection)을 이해하고 파이프 처리를 쪼개보면 재미있다.</p><p><code>명령어1 | 명령어2</code> 는 아래와 같다고 볼 수 있다.</p><div class="language-bash codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-bash codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">$ 명령어1 </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> 임시파일</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ 명령어2 </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> 임시파일</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">rm</span><span class="token plain"> 임시파일</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="프로그래밍-언어에서-stdout--stderr-구분하여-출력-방법">프로그래밍 언어에서 <code>stdout</code> , <code>stderr</code> 구분하여 출력 방법<a class="hash-link" href="#프로그래밍-언어에서-stdout--stderr-구분하여-출력-방법" title="제목으로 바로 가기">​</a></h2><p>표준 스트림이라면 프로그래밍 언어에서는 어떻게 구현되어 있을까?</p><ul><li><p><code>Node.js</code></p><div class="language-js codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-js codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 표준 출력에 메시지 출력</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"이것은 표준 출력 메시지입니다."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 표준 에러에 메시지 출력</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">error</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"이것은 표준 에러 메시지입니다."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAl0lEQVQImQXBXQ+BUACA4fOPGjaZDtWJ6ONIjC7M17iwSdPa6MZFNvzs1/OI07Vilb84Vg3ZpWaWbUmihLEK6bQlhtHHaElEtr8wPZSk5xK9K3DCFM8ZM3QCun2FdAJMy0ccbjX6/mFdfUjyBhmluMpHeRrT8pBuSE/6iOPtybz8snn8WBZvpF5gj3xcFWNaCtuL6Q0m/AEf804WJaZpfgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="558" height="240"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.9cec8a1.558.png" srcset="/assets/ideal-img/1.9cec8a1.558.png 558w" width="558" height="240"></noscript></div></div></li><li><p><code>Go</code></p><div class="language-go codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-go codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">package</span><span class="token plain"> main</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"fmt"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"os"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">func</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 표준 출력에 메시지 출력</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    fmt</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"이것은 표준 출력 메시지입니다."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 표준 에러에 메시지 출력</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token boolean" style="color:#36acaa">_</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> fmt</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Fprintln</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Stderr</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"이것은 표준 에러 메시지입니다."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// 에러 처리</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">panic</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAk0lEQVQImUXMyw7BQABA0f5RY4Fk1AxVNaaC8YpXQjyiIVUlxAKx62dfS+cDjrM/ZgziJ8v0zSJ5YewMG/UJfEOpqHBdgVvwcObrA3qV0ttm9DYZSlsCFSJVm5JoIGQLITXOLnlgrznTe874luN1RgR+i1BbhAyRvqFaj3DWcUb39GFy+TI8f/GMRaomqtamXPmPPyNuThM7sGELAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="572" height="239"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.777e950.572.png" srcset="/assets/ideal-img/2.777e950.572.png 572w" width="572" height="239"></noscript></div></div></li><li><p><code>C#</code> (실제로 확인 안 해봄)</p><div class="language-csharp codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-csharp codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">using</span><span class="token plain"> </span><span class="token namespace" style="opacity:0.7">System</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Program</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">static</span><span class="token plain"> </span><span class="token return-type class-name keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// 표준 출력에 메시지 출력</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        Console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WriteLine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"이것은 표준 출력 메시지입니다."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// 표준 에러에 메시지 출력</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        Console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Error</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WriteLine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"이것은 표준 에러 메시지입니다."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p><code>PHP</code> (실제로 확인 안 해봄)</p><div class="language-php codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-php codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token php language-php delimiter important">&lt;?php</span><span class="token php language-php"></span><br></span><span class="token-line" style="color:#393A34"><span class="token php language-php"></span><span class="token php language-php comment" style="color:#999988;font-style:italic">// 표준 출력에 메시지 출력</span><span class="token php language-php"></span><br></span><span class="token-line" style="color:#393A34"><span class="token php language-php"></span><span class="token php language-php keyword" style="color:#00009f">echo</span><span class="token php language-php"> </span><span class="token php language-php string double-quoted-string" style="color:#e3116c">"이것은 표준 출력 메시지입니다.\n"</span><span class="token php language-php punctuation" style="color:#393A34">;</span><span class="token php language-php"></span><br></span><span class="token-line" style="color:#393A34"><span class="token php language-php" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token php language-php"></span><span class="token php language-php comment" style="color:#999988;font-style:italic">// 표준 에러에 메시지 출력</span><span class="token php language-php"></span><br></span><span class="token-line" style="color:#393A34"><span class="token php language-php"></span><span class="token php language-php function" style="color:#d73a49">fwrite</span><span class="token php language-php punctuation" style="color:#393A34">(</span><span class="token php language-php constant" style="color:#36acaa">STDERR</span><span class="token php language-php punctuation" style="color:#393A34">,</span><span class="token php language-php"> </span><span class="token php language-php string double-quoted-string" style="color:#e3116c">"이것은 표준 에러 메시지입니다.\n"</span><span class="token php language-php punctuation" style="color:#393A34">)</span><span class="token php language-php punctuation" style="color:#393A34">;</span><span class="token php language-php"></span><br></span><span class="token-line" style="color:#393A34"><span class="token php language-php"></span><span class="token php language-php delimiter important">?&gt;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p><code>C++</code> (실제로 확인 안 해봄)</p><div class="language-cpp codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-cpp codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token macro property directive-hash" style="color:#36acaa">#</span><span class="token macro property directive keyword" style="color:#00009f">include</span><span class="token macro property" style="color:#36acaa"> </span><span class="token macro property string" style="color:#e3116c">&lt;iostream&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">int</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 표준 출력에 메시지 출력</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">cout </span><span class="token operator" style="color:#393A34">&lt;&lt;</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"이것은 표준 출력 메시지입니다."</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;&lt;</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">endl</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 표준 에러에 메시지 출력</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">cerr </span><span class="token operator" style="color:#393A34">&lt;&lt;</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"이것은 표준 에러 메시지입니다."</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;&lt;</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">endl</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마치면서">마치면서<a class="hash-link" href="#마치면서" title="제목으로 바로 가기">​</a></h2><p>그동안 추상적으로 알던 <code>stdin</code> , <code>stdout</code> , <code>stderr</code> 를 좀 더 명확히 알 수 있었다. 어찌 보면 이걸 이제서야 제대로 알게 된 것도 부끄럽다.</p><p>터미널의 일회성 명령어를 사용할 때는 정상 출력, 오류 출력이 함께 표시되어도 별 문제가 없지만, 스크립트를 실행하거나 데몬과 같이 지속적으로 수행되는 프로그램일 때는 표준 스트림을 목적에 맞게 적절히 사용해야 할 것이다.</p><p>이 내용을 바탕으로 프로그램 만들 때 더 신중하게 출력 방식을 선택할 수 있게 되었다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="reference">Reference<a class="hash-link" href="#reference" title="제목으로 바로 가기">​</a></h2><ul><li>Hub<ul><li><a href="https://www.daleseo.com/shell-redirection/" target="_blank" rel="noopener noreferrer">https://www.daleseo.com/shell-redirection/</a></li></ul></li><li>친절한 설명과 <code>Linux</code> 의 입출력 기본적인 내용에 대해서 알 수 있습니다.<ul><li><a href="https://inpa.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-devnull-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98-%EA%B8%B0%ED%98%B8-%EC%A2%85%EB%A5%98" target="_blank" rel="noopener noreferrer">🐧 /dev/null 2&gt;&amp;1 명령어 의미 - 완벽 이해하기</a></li><li><a href="https://inpa.tistory.com/entry/LINUX-%F0%9F%93%9A-%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%9E%AC%EC%A7%80%EC%A0%95-%ED%8C%8C%EC%9D%B4%ED%94%84-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%A0%95%EB%A6%AC" target="_blank" rel="noopener noreferrer">[LINUX] 📚 입출력 재지정 &amp; 파이프 명령어 💯 정리</a></li></ul></li><li>위키백과: 리다이렉션 설명
<a href="https://ko.wikipedia.org/wiki/%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98" target="_blank" rel="noopener noreferrer">리다이렉션</a></li></ul>]]></content>
        <category label="표준 스트림" term="표준 스트림"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Notion DB 동적 속성 방법]]></title>
        <id>/2024/03/23/notion-db-dynamic-properties</id>
        <link href="https://parkgang.github.io/blog/2024/03/23/notion-db-dynamic-properties"/>
        <updated>2024-03-23T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Notion 의 DB 는 JOIN 이 안된다. 같은 목적의 DB 인데 type 에 맞게 다른 속성을 저장하고 싶으면 어떤 방법이 있을까?]]></summary>
        <content type="html"><![CDATA[<p><code>Notion</code> 의 <code>DB</code> 는 <code>JOIN</code> 이 안된다. 같은 목적의 <code>DB</code> 인데 <code>type</code> 에 맞게 다른 속성을 저장하고 싶으면 어떤 방법이 있을까?</p><p>사례로 <code>일기</code> 를 저장하는 <code>DB</code> 가 있고 하루하루 체크하고 싶은 것이 있다고 가정해보자.</p><p>2월 동안은 <code>미라클 모닝</code> 을 진행하고 있어 얼마나 성공했는지 기록하고 싶다. <code>DB</code> 스키마는 아래와 같을 것이다.</p><table><thead><tr><th>속성</th><th>타입</th></tr></thead><tbody><tr><td>제목</td><td>텍스트</td></tr><tr><td>날짜</td><td>날짜</td></tr><tr><td>미라클 모닝 성공 여부</td><td>체크박스</td></tr></tbody></table><p>3월에는 <code>다이어트</code> 를 하고 있다. 목표 식단을 얼마나 달성했는지 보고 싶다. 이전 <code>미라클 모닝</code> 과 동일한 스키마인데 <code>식단 여부</code> 속성만 달라지는 데이터가 생긴다.</p><p>또다시 <code>식단 성공 여부</code> 를 저장하는 <code>DB</code> 를 만들기엔 관리 비용도 높고 날짜를 합쳐서 볼 방법이 없다. (<code>Notion Calender</code> 가 있다고 해도 무한대로 등록해서 볼 수 없음)</p><p><code>JOIN</code> 이 가능하다면 손쉽게 해결될 수 있지만 아쉽게 <code>Notion</code> 에선 지원하지 않는다. 이에 대한 나만의 해결 방법을 공유한다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="해결-방법">해결 방법<a class="hash-link" href="#해결-방법" title="제목으로 바로 가기">​</a></h2><p>단순하다. 하나의 <code>DB</code> 에 <code>prefix</code> 를 붙여서 속성을 구분하는 것이다.</p><p>나의 경우 <code>DB</code> 마다 다르지만 <code>그룹핑 &gt; 내용</code> (e.g. 운동 &gt; 러닝) 의 형태로 구분하고 있다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="insight">Insight<a class="hash-link" href="#insight" title="제목으로 바로 가기">​</a></h2><p>이렇게 운영하며 느낀 <code>Insight</code> 는 아래와 같다.</p><ul><li><p>일반 필터에서는 <code>다중 선택</code> 이 <code>OR</code> 로 동작하기에 <code>AND</code> 조건을 보고 싶으면 <code>고급 필터</code> 사용</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAiElEQVQImR2KwQnDMBAEVXhIAYE0kYchfRhcgH8GFyDFRrJ0OWzp7jZEC8M8Zl1K8UFEnya6qGE1s1VVu0V12amFXOjpiOjFZ0UTRb1OiAjMDKKKVBhbqSD6Do6ZB+0RuGrtp/+u2rDtCaYCZn477/2tlDzn4xhLyVPORyfGOAUfxpjiHEK4/wBftZVqYRyQfwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2864" height="1294"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.260a343.2864.png" srcset="/assets/ideal-img/1.260a343.2864.png 2864w" width="2864" height="1294"></noscript></div></div><blockquote><p><code>일반 필터</code> 는 <code>OR</code> 조건임</p></blockquote><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAnklEQVQImV3NMQ6DMAyF4RyYscdhhgWpM3AALgFDA+nQSrQ4CcSJHVdl6NAnfXrjr17r8wLwHvYD2xCpZ+ae6Pft5sMAYC/KWlcdASVnFmaWzFlyzsKcZcckD0sC1lXKOVcSkRCRj4iYUkIiQu89ruvqYzgEYCtVCKGWv8UYT0TprADYWk3TVNyN6YxZGmOW65fWt9OsdTPPczeOY/EBnqK36z6gPV4AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2820" height="1454"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.9c48beb.2820.png" srcset="/assets/ideal-img/2.9c48beb.2820.png 2820w" width="2820" height="1454"></noscript></div></div><blockquote><p><code>고급 필터</code> 에서 <code>AND</code> 조건 만들 수 있음</p></blockquote><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAaElEQVQImTWMSQrEMAwE8/93xpg5xCZavdKDEnIoqQ9FHWMMBKoKN0MpF2otMDOYCpo7eu843B0BEUFEkFJCzhnMBPqd8JBDbK2BmSHCT+UtVpgq7ovg1t5inBBDmnNi74211rO/H/wBaYGavDvRKZEAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="958" height="360"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.f54daf5.958.png" srcset="/assets/ideal-img/3.f54daf5.958.png 958w" width="958" height="360"></noscript></div></div><blockquote><p><code>AND</code> 필터가 많아서 불편하면 사진과 같이 여러 개 만들고 선택 안 해놓으면 됨 그럼 사진 기준으로 <code>A &gt; 2</code> , <code>A &gt; 3</code> 둘 다 있는 것만 선택됨</p></blockquote></li><li><p><code>다중 선택</code> 에서 <code>&gt;</code> 에 따른 데이터 구분은 <code>다중 선택</code> <code>정렬</code> 혹은 <code>검색</code> 을 통해 달성할 수 있음</p><ul><li>첫 번째 텍스트가 같은 것은 정렬되어서 보일 것이기 때문</li></ul></li><li><p>추가, 삭제, 이름 변경은 큰 문제 없음</p></li><li><p><code>다중 선택</code> 에서 일부분의 값 변경 시 테크닉</p><ul><li><p><code>CSV</code> 로 추출 후 <code>다중 선택</code> 속성을 변경 후 <code>CSV 머지</code> 하면 해당 부분만 바뀌지 않을까 했지만 <code>CSV</code> 에 정의된 데이터가 또 생성되면서 중복 데이터 발생해서 안 됨</p></li><li><p><code>버튼</code> 을 이용해서 <code>다중 선택</code> 의 일부분 값 <code>추가</code> , <code>제거</code> 가능</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAkUlEQVQYlU2PXQ4CIQyEuf/NPIVvvrlRWPpLGVPYqCRfpkmHTluYCe6OMcalm6xjOFgdj5eh1PrGcTxBdEKEIUKXMkwZRyXc7g1FVdBaQ8TAnBMRgRmxNMEMAIFCtCfkh8QsUbjbwswgqigkBuId1XvH2ftqfqfPufYvzNuQLw8g3aZ/Io2qitbqiv1dvGP/+QBbrRIptLHCuQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1220" height="844"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.03c4538.1220.png" srcset="/assets/ideal-img/4.03c4538.1220.png 1220w" width="1220" height="844"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAS0lEQVQImTWMUQrAMAhDvf9VO+jWTUXUZljw4/0kL6HMRETAzCCqWO8HEQaLwN2x90Y5VBIzY86Jawys+zmjorqGVPWEtWq6rLfmBynedYx97ZxrAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1936" height="694"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.6898709.1936.png" srcset="/assets/ideal-img/5.6898709.1936.png 1936w" width="1936" height="694"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAdklEQVQImU2OSQ5DIQxDuf9N+1H5QAZmXCWrWnoby4kd9jkousAiUFWIMEQERIRSK0QbmAlhrQWiipwzzjmOeSqMlxRPEZhCKRnP88He241/jTFw73VCjBFvSui9e7XRWvNqa1prOh5M6Ys5p4cN+2QHzOwzjB/QWMJi4enkPQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="706" height="400"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.b2c8a39.706.png" srcset="/assets/ideal-img/6.b2c8a39.706.png 706w" width="706" height="400"></noscript></div></div><blockquote><p>여기서 <code>토글</code> 은 기존에 값이 있으면 제거하고 없으면 추가하는 동작임</p></blockquote><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAABYlAAAWJQFJUiTwAAAASUlEQVQImV3MUQ6AMAwC0N3/turWdkKL2Uz8kOQFvmjn1RXhcrdt7TlDJEViA261YSF3FwCZmY4+BFArVfVp75urKpWZArn77wE4b3Xaa4+oOQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1178" height="332"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.ca7dd55.1178.png" srcset="/assets/ideal-img/7.ca7dd55.1178.png 1178w" width="1178" height="332"></noscript></div></div></li></ul></li><li><p><code>동적 필드</code> 보기가 어려우면 동적 필드 목적에 맞는 <code>보기</code> 를 만들어서 달성 가능</p></li><li><p><code>동적 필드</code> 의 일부분만 다른 DB으로 뽑고 싶으면</p><ul><li><code>깡통 DB</code> 로 이동 시 선택된 값만이 아닌 <code>속성 통체</code> 로 <code>복사</code> 된다.<ul><li>깡통 DB에 부어넣어서 달성할 수 있는데 아쉽게도 <code>다중 선택</code> 에서 선택된 값만 이동되는 것은 아니고 <code>다중 선택</code> 속성이 통체로 복사됩니다.</li><li>즉, <code>다중 선택</code> 에 <code>A</code> , <code>B</code> , <code>C</code> , <code>D</code> 가 정의되어 있고 이동하려는 페이지는 <code>A</code> , <code>B</code> 만 선택되어 있더라도 옮기기 시 <code>다중 선택</code> 의 <code>A</code> , <code>B</code> , <code>C</code> , <code>D</code> 값이 모두 이동됩니다.</li></ul></li><li><code>A DB</code> 의 페이지를 <code>B DB</code> 로 <code>옮기기</code> 시 <code>B DB</code> 에 없는 이름의 속성만 추가됨<ul><li><code>A DB</code> → <code>B DB</code> 로 이동 시 <code>B DB</code> 에 <code>A DB</code> 와 동일한 이름의 속성이 있으면 <code>B DB</code> 에 정의된 한정해서 표시됨 그렇기에 <code>A DB</code> 에 있는 값이 옮겨졌다는 이유로 <code>B DB</code> 에 추가되지는 않음</li><li>즉, <code>이전 DB</code> 에서 <code>A</code> , <code>B</code> , <code>C</code> 를 정의 및 선택하고 <code>이동하려는 DB</code> 에 <code>A</code> , <code>B</code> 만 정의되어 있다면 옮기기 후 <code>A</code> , <code>B</code> 만 보일 것입니다.</li></ul></li></ul></li><li><p>속성 데이터가 지저분하면 <code>CSV</code> 로 뽑아서 가공해서 보는 방법이 있음</p><ul><li>헷갈리면 안 되는 것이 <code>CSV</code> 는 <code>데이터 스키마</code> 에 해당하는 껍데기이므로 가공 후 <code>import</code> 하더라도 페이지에는 내용이 없음으로 <code>CSV</code> 로 가공 후 모든 것을 할 수 있다는 생각은 주의하기</li></ul></li><li><p>속성이 너무 많아진다 싶으면 <code>보기</code> 별로 <code>export</code> 해서 <code>아카이브 처리</code> 하는 방법도 있을 것</p></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마치며">마치며<a class="hash-link" href="#마치며" title="제목으로 바로 가기">​</a></h2><p>이런 고민을 통해 데이터 설계, 관리 방법에 대해 생각할 수 있었다.</p><p>단순한 해결 방법 중 하나지만 누군가는 <code>Notion</code> 을 사용하며 나와 같은 고민을 했을 수 있다.</p><p>이번 글을 통하여 해당 방법으로 해결하여 실제 사용 중인 사람이 있구나 공유되기를 바란다.</p>]]></content>
        <category label="Notion" term="Notion"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[parkgang.log(2023)]]></title>
        <id>/2024/01/14/2023-retrospective</id>
        <link href="https://parkgang.github.io/blog/2024/01/14/2023-retrospective"/>
        <updated>2024-01-14T06:00:00.000Z</updated>
        <summary type="html"><![CDATA[새해가 되었다 2023년을 회고해봐야지… 이번에는 반성문이다.]]></summary>
        <content type="html"><![CDATA[<p>새해가 되었다 2023년을 회고해봐야지… 이번에는 반성문이다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAIAAACExCpEAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAxUlEQVQYlWWOzW6FIBBGef93cuVSosU9Ff9auSBTrhXCLTAN2l1PJrOYL5nvEKVU0zRd11FK27allPZ9X1VVXdfGGOKcexdCa70si1JqXddt2wBAShlCIJ/zbDhHxJwz/oMMnMueZe9jzikW0s3rVeKRcz0KPI54fmfEe37sVzzPEk/DANOEiBEgWpucL/t43k3kY569evxVpYQh4PUWLxVi9n0SwloLxrwx1jEGALvW4zh670kI4XTuNldKPaRMqSi66/gLqLH82EOy8J4AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="800" height="670"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/thumbnail.784b1b5.800.png" srcset="/assets/ideal-img/thumbnail.784b1b5.800.png 800w" width="800" height="670"></noscript></div></div><blockquote><p>출처: <a href="https://commons.wikimedia.org/wiki/File:DKsrp.png" target="_blank" rel="noopener noreferrer">Wikimedia Commons File:DKsrp.png</a></p></blockquote><p>작성 전 2022년 회고를 읽어보는데 마치 어제 있었던 일처럼 생생하다. 나이가 들수록 시간이 빨리간다는데 체감이 많이 되고있다.</p><p>2023년은 나에게 <code>더닝 크루거</code> 의 <code>고점</code> 을 찍고 <code>급속도</code> 로 내려오는 시기이었다.</p><p>이미 <code>고점</code> 을 찍은 줄 알았지만 아쉽게도 아니었다… 덕분에 느낀 것도 많고 생각도 많다 왜 그런지는 차차 글로 풀어보자</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="tldr">TL;DR<a class="hash-link" href="#tldr" title="제목으로 바로 가기">​</a></h2><ul><li><code>산업기능요원</code> 을 받아 <code>병특</code> 이 시작되었다.</li><li><code>여행</code> 을 3번 다녀왔다. 열심히 돌아다녔네<div class="imgContainer_g3cu spacer_xInT" style="width:40%"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAABYlAAAWJQFJUiTwAAABG0lEQVQYlRXKay9CAQCA4fPLjREtVh/asKm2tFLR0T3KYqWrLEa30SzV6XbQRSmd47Wez48AMJ0MkaQ2g4GM3JfpddvI/Q5dqcn0e7QqCPn6knilyV01R/wpQvAhi5iaIOYmiJkxYm5G/lVBsCXmrPlU7O4s3ug+hqIdbURC41XZ8S9Y98yxJecI7syczQA4PPe4ohZMRQcH4QK6IOhDM7Z9Cs70H4JrFUUFc7qB8/mYk4IVY7iMNgi6gILxskLwsbSKCzTnS0z5d6xXKQ5PK+xFftB7vzi6SOFKWMjXYwj22wUb4oLd0BCNX2HLt8TgqXHmc2KP3WCOJrguJREaA5VSS6XWnVFt/1JuqbxJHeTPFzq9Ib2PAfJozD+Oiw0Pg7XaxAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="884" height="689"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.c28ee3a.884.png" srcset="/assets/ideal-img/1.c28ee3a.884.png 884w" width="884" height="689"></noscript></div></div></li><li><code>영어</code> 와 <code>코딩 테스트</code> 공부를 시작했다.</li><li><code>자전거</code> 새로운 코스와 최장 거리 기록을 달성했다.</li><li><code>PKM</code> 을 개선하고 <code>글</code> 로 작성했다.</li><li><code>부서</code> 이동을 했다.</li><li>처음으로 오프라인 <code>컨퍼런스</code> 에 참여했다.</li><li>군대 <code>훈련소</code> 를 다녀왔다.</li><li><code>공부 방법</code> 과 <code>바른 자세</code> 에 대해서 고민을 시작했다.</li><li>꾸준히 <code>운동</code> 했다.</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="dashboard-돌아보기">Dashboard 돌아보기<a class="hash-link" href="#dashboard-돌아보기" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="til">TIL<a class="hash-link" href="#til" title="제목으로 바로 가기">​</a></h3><p>작년에 <code>529</code> 개에 비해서 <code>616</code> 개로 <code>87</code> 개 증가했다. 여전히 새롭게 배운 것은 많구나</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAl0lEQVQImUXNS27DMAwAUd3/mkU2rh1bpCjqmymkLkKAG2LwGEQVM8M9M8eg945aIaky56CPwfE4ofaKqqCqlFIYY2CWiOfB6J1aGzU7obRCSgmRuNXPZ5JzJpmxJrtxn78rXKLueGlz/ofyvGmtUWtB7pNgxbiuc6+773jpx+uHpLJv93ERSv2+XvIKlxhjRETwbKQo/AHp/uhZv9rnAgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3222" height="2070"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.f5402ed.3222.png" srcset="/assets/ideal-img/2.f5402ed.3222.png 3222w" width="3222" height="2070"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="github-commit">GitHub Commit<a class="hash-link" href="#github-commit" title="제목으로 바로 가기">​</a></h3><p><code>여행</code> 과 <code>훈련소</code> 를 가느라 곳곳 빈 곳이 보인다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAsElEQVQImVWPPQrCQBSE97xewVvoARQtRUEQBMXaSkUEGy2iYogaE/Njkk1295MEA/rBNMPMe4zItMSVHjXGGLTRlR7+k/AdkRcFws08xvaMyX3BNXP+CkmW4r18kjRBlKbUOdtgT+8ypH8ZcYytKviLKM/y9aSSLN017WOXgTXFiVxWpx1BHCKegc/Nf2DqNKCM4uzZtDYdGvMme+eAKAqFlDnlp3pITapSAhWiMXwAc4PjDRdgbm8AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1484" height="932"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.5625ea5.1484.png" srcset="/assets/ideal-img/3.5625ea5.1484.png 1484w" width="1484" height="932"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="코딩-테스트">코딩 테스트<a class="hash-link" href="#코딩-테스트" title="제목으로 바로 가기">​</a></h3><p><code>하반기</code> 부터 풀기 시작했는데 빼먹지 않고 주기적으로 학습을 달성할 수 있었다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAWElEQVQImVXMOw6AIBAAUe5/MCsTCy9gISAQfsLSjtHCaPGaKUYdzlPPRpdB6/IjMnA5YktA3UHbg12blzaW3Ri8DUzbwpxWVEyZXOpz/Goi1N7xZ8K3zAU6aHFJ3yayAQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2312" height="668"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.36031d2.2312.png" srcset="/assets/ideal-img/4.36031d2.2312.png 2312w" width="2312" height="668"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="영어-공부">영어 공부<a class="hash-link" href="#영어-공부" title="제목으로 바로 가기">​</a></h3><p>이것도 <code>훈련소</code> 가느라 도중에 <code>연속 학습</code> 이 중단되어 아쉬웠다. 그래도 꾸준히 달성할 수 있었다.</p><p>영어 공부 시간을 더 늘리고 싶은데 아직 여유 시간 확보가 안돼서 늘리지 못하고 있다. 다음 연도에는 영어 공부 시간 비중을 확 늘리고 싶은 바램이 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAABRElEQVQYlR2RO07DQAAFt+UkXICCW3AM6OhoqOhpoKJFokMUCBoEgRA+AgIS4PxxYmIkK7Fj78Zes/nYHkS6V87ME9Y45E26VKMuzVjT02AnOXZS0NVwF1S4HVQQr7JHefTEpV/iSTbpptBNC/opXPs37DVWWa8sIdpqTs3XNAJDLYipB5paoHESeJY2W9Vldu9XEPetPielBy5ePjl//OD07p1by6YznvOdwpc0WL5BtMOUhp9Q9zVNb0D7x6YZGXrmHwE6CbTHOeLIKti+mnBgwXHD5aN3hkozvJHGkzG/OQwnBWKnNGHtULJZgo0y7H9BEoMaJYQygjxDzkC0FFTdmJYX46U5AwOpCYlUiPqdMs0KfFMgPDMjlC6xkuQzQwbMozpq6BCphNnEMDIZwtHwY1gY/m9HFzgGXMOi5bcuFif8AXgyaw3KBxT/AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1500" height="1500"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.a1d98fb.1500.png" srcset="/assets/ideal-img/5.a1d98fb.1500.png 1500w" width="1500" height="1500"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="운동">운동<a class="hash-link" href="#운동" title="제목으로 바로 가기">​</a></h3><p><code>웨이트 트레이닝</code> 을 꾸준히 해서 <code>1년 차</code> 를 맞이할 수 있었는데 눈에 핏줄도 터지고 <code>훈련소</code> 가기 전 무리를 해서 어깨도 고장 나고 여러모로 <code>부상</code> 이 있었던 한 해이었다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="독서">독서<a class="hash-link" href="#독서" title="제목으로 바로 가기">​</a></h3><p>아쉽게도 한 권 읽었다.</p><p>년초에 <code>Go 언어를 활용한 네트워크 프로그래밍</code> 읽으려고 <code>30분</code> 씩 시간 확보를 했는데 <code>야근</code> 및 <code>프로젝트</code> 진행 때문에 다른 <code>새로운 공부</code> 로 인하여 <code>우선순위</code> 에서 계속 밀렸다.</p><p>2개 모두 적절하게 잡을 수 있도록 <code>우선순위</code> 를 조절했어야 했는데 개인적으로 잘못 판단한 부분이라고 생각한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAABYlAAAWJQFJUiTwAAAARUlEQVQImR3LOQ6AMAwF0dz/mFSkoEHZbH9nEKlGetKUWm/e1hkm+jTMHSnITPbehJLrWZQxFyEd8BBmhpud/kOEaGPxAXn4TiSgC4JEAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2760" height="558"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.d412be4.2760.png" srcset="/assets/ideal-img/6.d412be4.2760.png 2760w" width="2760" height="558"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="여행">여행<a class="hash-link" href="#여행" title="제목으로 바로 가기">​</a></h3><p><code>3번</code> 으로 꽤 많이 다녀올 수 있었다. <code>해외</code> 랑 <code>국내</code> 적절히 조화되어서 좋았다.</p><p><code>일본</code> 도 이전에 다녀왔던 곳이라 다음엔 <code>해외</code> 로 새로운 곳을 가보고 싶다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAQ0lEQVQImR3KUQoAIQhAwe5/0wUhyEpD7S27nwPTRARdxvbA/BARVBX3FllFZPCMoM05UVUyi+PO5zUHNw2zTe/y5xd48k3bKpibDQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3246" height="680"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.71ebaf8.3246.png" srcset="/assets/ideal-img/7.71ebaf8.3246.png 3246w" width="3246" height="680"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="블로그-글">블로그 글<a class="hash-link" href="#블로그-글" title="제목으로 바로 가기">​</a></h3><p><code>5개</code> 로 많이 작성하지는 못했다.</p><p>워낙에 쓰고 싶은 것이 많았지만 이것도 <code>우선순위</code> 조절 실패로 밀리고 밀려 <code>레거시 글</code> 만 가득 남게 되어 결국 안 쓰게 되더라</p><p>대충이라고 글을 써보려고 했지만 성격상 대충은 안되고… 대충이라고 생각하는 것도 많은 시간이 소모되기에 정말 가치가 있는 글만 뽑아서 매달 한 개씩 작성하는 것을 목표로 잡아보는 것이 현실적이라고 느낄 수 있었다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAi0lEQVQImUVP2QoCMRDr//+iDwpiV9vqdi9XnSOSFnEgZCZkrhBjRMoFDDOHeYe7N+0XQUQwTILzqLhOirIahkmRV8P+cWxvx0scQVSRFkV9WsNjs2a+rz0nU2/GUxEci+A29wYayCOx9TqoGWJVXKoiL9ZWzXtfS6aRWhDRNu2Q/lN5I5m38Sk+9wUEaOjK7bHocgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="866" height="532"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.0d0fc4a.866.png" srcset="/assets/ideal-img/8.0d0fc4a.866.png 866w" width="866" height="532"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="타임라인">타임라인<a class="hash-link" href="#타임라인" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="산업기능요원-시작">산업기능요원 시작<a class="hash-link" href="#산업기능요원-시작" title="제목으로 바로 가기">​</a></h3><p>커리어가 진행 중인 상태라 도중에 <code>군대</code> 에 들어가면 많이 꼬일 수 있어 <code>산업기능요원</code> 이 간절했다.</p><p>이걸 위해 <code>정보처리기사</code> 도 한 번에 취득했지만 기타 문제로 <code>편입 신청</code> 에 계속 문제가 발생하자 정말 군대를 가는 건가? 하고 마음이 복잡했다.</p><p>다행히 <code>2023년</code> 초에 <code>산업기능요원</code> 으로 <code>편입</code> 이 되어 한시름 놓을 수 있었고 확정되는 그 순간의 기쁨은 말로 이루 말할 수 없었다.</p><p>하지만… 이제 시작이었다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="대한민국-동쪽-여행">대한민국 동쪽 여행<a class="hash-link" href="#대한민국-동쪽-여행" title="제목으로 바로 가기">​</a></h3><p>이전부터 <code>병특</code> 이 되면 <code>기념 여행</code> 을 가자고 생각했었다.</p><p>그래서 <code>병특</code> 이 되자마자 <code>여행</code> 을 준비하게 되었고 <code>대한민국 동쪽 여행</code> 이라는 컨셉을 가지고 출발하였다.</p><p>당시 <code>1월</code> 인지라 <code>한파특보</code> 가 떨어지고 엄청 추운 상황이어서 이거 여행 갈 수 있는 건가 싶었는데 까짓것 이런 기쁨을 이럴 때 표출하지 않으면 언제 느끼겠어 하고 강하게 드라이브를 걸었다.</p><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAIAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAME/8QAIhAAAQQBAgcAAAAAAAAAAAAAAgABAwURBBIGEyExUVNh/8QAFAEBAAAAAAAAAAAAAAAAAAAAAv/EABkRAAIDAQAAAAAAAAAAAAAAAAECABEikf/aAAwDAQACEQMRAD8AyV3FFVt1cE1XPzIiYQIJBJiyzPl/Hf6oFcaXc+K/pn2siIKps6PYmYUMif/Z&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1024" height="768"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9-1.828d02f.1024.jpeg" srcset="/assets/ideal-img/9-1.828d02f.1024.jpeg 1024w" width="1024" height="768"></noscript></div></div></td><td><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAANAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAABQb/xAAkEAABBAAGAQUAAAAAAAAAAAACAQMEEQAFBgcSMRMVITJB0f/EABUBAQEAAAAAAAAAAAAAAAAAAAME/8QAGxEAAgMAAwAAAAAAAAAAAAAAAREAAgMEFCH/2gAMAwEAAhEDEQA/AE9HbeaajP8AkCF6s4VmAy0UuIdfBFpftOVYksy0JADMZQR9H54TIumgKEkuKja1VjdVi+bckz94s3ZOZJFtpgABEJFpOIkqdVVr1XaXeEnojoumKSVWiVPdoPzEh5WtSz64/VysEkp//9k=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="768" height="1024"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9-2.9ab1cdd.768.jpeg" srcset="/assets/ideal-img/9-2.9ab1cdd.768.jpeg 768w" width="768" height="1024"></noscript></div></div></td></tr></tbody></table><p>여행 내내 정말 정말 정말 추웠고, <code>포항</code> 쪽은 추운 수준이 아니라 <code>동상</code> 걸리는 느낌을 받았다.</p><p>그래도 <code>한파</code> 라 사람도 없는 거리를 <code>겨울</code> 의 쌀쌀한 느낌을 받으며 거리를 운치 있게 돌아다니며 생각 정리를 할 수 있었다. 오히려 너무 추워서 <code>아련하고</code> , <code>센치한</code> 느낌이 난다랄까?</p><p><code>겨울</code> 하면 <code>동쪽 여행</code> 이 가장 기억날 거 같다. 즐거웠다!</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="영어-공부-시작">영어 공부 시작<a class="hash-link" href="#영어-공부-시작" title="제목으로 바로 가기">​</a></h3><p>코딩은 재밌는데 영어를 잘못하니까 팍팍 읽고 나아가야 하는데 영어가 나오면 맥이 끈 키니 코딩하면서 짜증이 나기 시작했다.</p><p>시니어로 가기 위한 <code>마일스톤</code> 중 영어는 필수라고 생각하기에 느리지만 꾸준하게 영어 공부를 하기로 하였다.</p><p>그래도 공부를 하니까 영어가 조금씩 보이기 시작해서 재미가 생기더라</p><p>나중에 영어를 잘하게 되었을 때 개발이 얼마나 더 재미있을지 설레어 공부가 더 재미있게 할 수 있었다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="첫-대학로-방문">첫 대학로 방문<a class="hash-link" href="#첫-대학로-방문" title="제목으로 바로 가기">​</a></h3><p>나는 뭔가 결정할 때 기존에 해본 것보다는 새로은 것을 좋아하는 편인다. <code>서울</code> 에 살면서 안 가본 곳 중 <code>대학로</code> 가 궁금하던 찰나 주기적으로 하는 <code>헌혈</code> 을 하면서 <code>연극</code> 티켓을 받게 되어 <code>대학로</code> 를 갈 수 있는 기회가 생겼다.</p><p>저는 <code>서울</code> 북부로 올라가면 뭔가 기분이 좋더군요.</p><p><a href="https://naver.me/IMQpQ7sa" target="_blank" rel="noopener noreferrer">칸다소바</a>에서 <code>아부라소바</code> 라는 것을 처음 먹어봤는데 너무 맛있어서 아직도 기억에 난다. 또 먹으러 가고 싶은데 거리가 있어서 어쩔 수 없이 <code>대학로</code> 에 갈 수 있는 기회가 만들어지면 좋겠다.</p><div class="imgContainer_g3cu spacer_xInT" style="width:40%"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAANAAoDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAwQFBv/EACMQAAIBAwMEAwAAAAAAAAAAAAECAwQFEQASMQYTIVFBYXL/xAAUAQEAAAAAAAAAAAAAAAAAAAAF/8QAHBEBAAICAwEAAAAAAAAAAAAAAQMRAAISFGEh/9oADAMBAAIRAxEAPwAS0k18Y1N9pYqazyREGmgnJlx8EucDI/POpklt6XopGpFv0wWAmIAwhiNvjkeDxyNZ+SpuaHtQXApAx37GiDHPvPBP2RpuPYY1LozsQMsXOSfeh45plXZHFenJoGvEPVu3yq+Z/9k=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="768" height="1024"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/10-1.5138256.768.jpeg" srcset="/assets/ideal-img/10-1.5138256.768.jpeg 768w" width="768" height="1024"></noscript></div></div><p>이외 <code>연극</code> 도 보고 <code>낙산공원</code> 가서 산책도 했는데 이때가 참 <code>운치</code> 있게 기억에 남는다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAIAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAUG/8QAIhAAAgEDAgcAAAAAAAAAAAAAAQIDAAURBhIEBxMhMXGh/8QAFAEBAAAAAAAAAAAAAAAAAAAABP/EABgRAQEBAQEAAAAAAAAAAAAAAAECABEh/9oADAMBAAIRAxEAPwDQjmLBa+8fDLP427ZMFvWR9qJJreV3Z10/cWDEkHrxDNKUSraeuSQSeb//2Q==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1024" height="768"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/10-2.027bb16.1024.jpeg" srcset="/assets/ideal-img/10-2.027bb16.1024.jpeg 1024w" width="1024" height="768"></noscript></div></div><p>새로운 곳에 가서 보고, 듣고, 경험하는 것은 역시 좋더라 이런 경험이 힘들 때 나에게 힘을 줄 수 있는 거 같다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="자전거-목표-갱신">자전거 목표 갱신<a class="hash-link" href="#자전거-목표-갱신" title="제목으로 바로 가기">​</a></h3><p><code>여름</code> 이 다가오면 <code>자전거</code> 시즌이 시작된다.</p><p><code>용산</code> 근처까지 <code>왕복</code> 거리를 자전거로 커버할 수 있게 되면서 슬슬 <code>남산</code> 을 가볼 수 있지 않을까? 생각이 들기 시작했고 도전을 해보았다.</p><p>처음 <code>업힐</code> 이 생각보다 힘들어서 끝까지 올라갈 수 있을까 걱정이 되었는데 이것도 하나 못 넘으면 어떻게 하냐라는 마인드로 첫 남산 업힐을 완주할 수 있었다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAANAAoDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAwQGB//EACYQAAECBQMDBQAAAAAAAAAAAAECEQADBAUSBiExE0FxFCIzkdL/xAAVAQEBAAAAAAAAAAAAAAAAAAABA//EABoRAAICAwAAAAAAAAAAAAAAAAECABESITH/2gAMAwEAAhEDEQA/ALBGt6GTK9FMtNzFUnBWKJIUrDuWB27eXgJ1CFFxZ7qx3+JP6jM6bV1aL5X9RCCEzEyhh7DixI458mHVVFzyLXerAfgBG32l4AD25N6VsWG5/9k=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="768" height="1024"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/11.a830975.768.jpeg" srcset="/assets/ideal-img/11.a830975.768.jpeg 768w" width="768" height="1024"></noscript></div></div><p>서울의 풍경이 쭉 보이는 <code>남산</code> 은 참 매력적인 곳 같다. 이후로도 <code>남산</code> 에 종종 <code>자전거</code> 를 타고 방문해서 <code>운치</code> 를 즐겼다.</p><p>다음 연도에는 또 새로운 <code>코스</code> 를 도전해 봐야겠다. 자극도 되고 즐겁다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="일본-북큐슈-여행">일본 북큐슈 여행<a class="hash-link" href="#일본-북큐슈-여행" title="제목으로 바로 가기">​</a></h3><p>대학교 졸업 과제 우승 상금으로 <code>일본 후쿠오카 여행</code> 을 다녀왔다.</p><p><code>벚꽃</code> 이 만개하는 시기에 <code>일본</code> 이라니… 더없이 행복했다… <code>벚꽃</code> 이 떨어지는 <code>온천</code> 에서 <code>마운틴 뷰</code> 도 즐기고… 마음 같아선 사진을 모두 올리고 싶지만 몇 개만 올려본다.</p><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAANAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAABAX/xAAlEAABAwIFBAMAAAAAAAAAAAABAgMEABEFEiExQQYiU3GR0fD/xAAUAQEAAAAAAAAAAAAAAAAAAAAD/8QAHREAAQQCAwAAAAAAAAAAAAAAAgABAwQREhMhUf/aAAwDAQACEQMRAD8AZ1Jh0OGlmXNKC4wSpsFelzyoDcVAONR2yUORmc6dFWa5+aBPntzTJDsZAjtBtSWkqtopWW1/3qiKwODmPdL38w+qSa/JYflEcZ8QV4BjDV+1/9k=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="768" height="1024"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/12-1.cb34220.768.jpeg" srcset="/assets/ideal-img/12-1.cb34220.768.jpeg 768w" width="768" height="1024"></noscript></div></div></td><td><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAANAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABgUI/8QAJhAAAQMCBQMFAAAAAAAAAAAAAQIDBAYRAAUSITETQXEVFkNhov/EABUBAQEAAAAAAAAAAAAAAAAAAAEC/8QAGhEAAQUBAAAAAAAAAAAAAAAAAQACAwQRMf/aAAwDAQACEQMRAD8ASUtVVKQcmZiZIWmYzCbBloBNiOSd9z3v35xa96ZUficP2ALH9Yz/AFBVbkuq2IKIjbanXkw1vlWpZ1LKQrgC48b8YYSMiESQ7H9Qmr6Ki3q1hN7G17AbeMJtyAboxSyoH8X/2Q==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="768" height="1024"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/12-2.19dd230.768.jpeg" srcset="/assets/ideal-img/12-2.19dd230.768.jpeg 768w" width="768" height="1024"></noscript></div></div></td></tr></tbody></table><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="개인지식관리-재정비">개인지식관리 재정비<a class="hash-link" href="#개인지식관리-재정비" title="제목으로 바로 가기">​</a></h3><p><code>PKM</code> 은 <code>개인지식관리(Personal knowledge management, PKM)</code> 를 말한다.</p><p>나는 <code>메모</code> 를 하면서 <code>성장</code> 하고 싶었다. <code>메모</code> 를 해야지 <code>측정</code> 도 가능하고 그 시절 그럤구나 하고 <code>회고</code> 할 수 있다고 생각하기 때문이다.</p><p>하지만 <code>메모</code> 하면서 <code>공부</code> 하는 건 쉽지 않았는데 <code>메모</code> 하고 <code>정리</code> 하는 것이 시간을 많이 소비하기 때문이다.</p><p>이걸 더 빠르게 할 수 있는 방법이 있지 않을까? 하고 대대적인 개선 작업을 진행했고 관련해서 <a href="/pkm/">PKM 🧠</a> 에 글로 작성해서 출시했다.</p><p>이제 탄력을 얻어서 빠르게 공부하고 정리하면서 복습하고 이런 환경이 되어서 성장하기를… 바라본다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="토렌트로-시작한-네트워크-공부">토렌트로 시작한 네트워크 공부<a class="hash-link" href="#토렌트로-시작한-네트워크-공부" title="제목으로 바로 가기">​</a></h3><p><code>토렌트</code> 의 인식이 <code>불법</code> 에 가까운 것으로 아는데 왜 그런지 문득 궁금해졌다.</p><p><code>Docker</code> 으로 <code>격리된</code> 환경을 만들고 <code>네트워크</code> 를 어떻게 하면 <code>안전한</code> 환경에서 다운로드 받을 수 있는지 궁금해서 아래의 흐름으로 찾아보게 되었는데</p><ol><li><code>토렌트</code> 다운을 위해서 <code>격리된 환경</code> 이 필요했고</li><li>격리된 <code>Docker</code> 환경에서 <code>암호화</code> 해서 통신하려고 하다 보니</li><li><code>Docker 네트워크 인터페이스</code> 에 대해서 궁금해지고</li><li><code>ISP</code> 가 추적 못하게 하려고 <code>VPN</code> 에 대해서 공부하다 보니 <code>암호화</code> 방법에 대해서 궁금해지고</li><li><code>암호화</code> 방법 이해하고 나니까 <code>TLS</code> 원리 복습하면서 <code>하이브리드 암호화</code> 에 대해서 이해하고</li><li>최종적으로 어떻게 <code>IP 추적</code> 이 일어나는지 한 바퀴를 봐버렸네</li></ol><p><code>토렌트</code> 다운로드로 시작해서 얻은 <code>CS</code> 지식들이 의미 있었고 너무 재미있었다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="나의-단짝-친구-chatgpt">나의 단짝 친구 ChatGPT<a class="hash-link" href="#나의-단짝-친구-chatgpt" title="제목으로 바로 가기">​</a></h3><p>프로젝트 진행 중 나의 <code>강점</code> 으로 <code>질문</code> 을 잘하는 것이라는 이야기를 들었다.</p><p>돌아보면 고등학교 시절 <code>모바일 로보틱스</code> 기능반 당시 궁금한 것이 너무 많아 하나라도 놓치고 싶지 않은 마음에 <code>강사 선생님</code> 께 물어볼 것을 미리 <code>질문</code> 으로 만들곤 했는데 이 <code>행동</code> 이 잘 이어진 거 같다는 생각이 들었다.</p><p>나는 매우 사소한 것일지라도 <code>호기심</code> 이 많았고 <code>질문</code> 할 것들은 늘 넘쳐났는데 이게 또 <code>사소</code> 하다 보니 사람 한 명 붙잡고 계속 물어보기 어려운 것들이 많았다.</p><p>이전까지는 <code>질문</code> 에 대답해 줄 수 있는 사람이 없으면 <code>구글링</code> 으로 여러 정보를 취합해서 머리에서 <code>프로세싱</code> 하거나, <code>Stackoverflow</code> 같은 곳에 질문을 올려서 해결하곤 했는데 <code>ChatGPT</code> 라는 선생님이 등장하면서 학습에 가속도가 붙는 느낌이 받았다.</p><p>이게 실력이 팍하고 늘어나는 느낌은 아니지만 사소한 것에 대해서 <code>호기심</code> 이 있다면 조금씩 계속 물어보면서 <code>파운데이터션</code> 지식이 확장되는 느낌이 강하게 들었다.</p><p>예시로</p><ul><li>영어 공부 중 아직 모르는 <code>단어</code> 와 <code>문법</code> 이 많지만 아는 것을 이용해서 <code>독해</code> 를 해보고 이것을 <code>ChatGPT</code> 에게 어느 부분이 틀렸는지, 그리고 왜 그런 뜻인지 물어보면 빈약한 문법은 무엇인지, 내가 틀린 부분은 어느 문법인지 알려주니 그 자체가 <code>꼬리</code> 를 물어서 추가적으로 검색하는 데 도움이 되었다.</li><li><code>코딩</code> 에서도 <code>CLI</code> 에 <code>출력</code> 되는 명령어 중 그냥 넘길 수 있는 것도 하나씩 물어보면서 나중에 저런 명령어 혹은 필요하다면 머리에 <code>indexing</code> 할 수 있었다. <code>npx create-next-app --ts</code> 하면서 나온 내용으로 아래와 같이 질문하였다.<div class="imgContainer_g3cu spacer_xInT" style="width:40%"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAYAAAALpr0TAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA6UlEQVQYlT2QyU7EMBBE80vMxI7jfYvHCTNCLOLA///HgxjBoVR1eN3qrmnfNvZcKNqQl5WsNNU4sjYUYwmLwgnJ1FLilhN7rRylsllP1ZaymuFBKuwsmW6t0XuneE8JgagN9iKwT/OQuwr89QT3g/3llRgCRgjcovBK4eQyAH8RuAH2g/v7J60fBGPwf+B5m1QD8hfJVEpj6wc5V1LMWLlghPz3kc+N2XmCcSQfSdYRtSZbRzKW6sPI0Vimnhsf26mNR6nc243n+ltZ9X4MnA9Oq+28pcpXCDxCosVCWDR+WTGzGLI/PX4Dm2ibluZvZF8AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="844" height="796"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/13-1.a5bc4d5.844.png" srcset="/assets/ideal-img/13-1.a5bc4d5.844.png 844w" width="844" height="796"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT" style="width:40%"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAkElEQVQImVWOyxLCIAxF+f+P1JFHaApSsBSLm+sk6sLFmUySk4cpuWOMgTGemPOFOadynv/RFDoRmRGIQfFDoAU5F7S2o2wVW20we+9wnuB8hHWkeB9xuVpYFzSXYdP7gZv1WpAB5oSUC9Z0R/oiW1WUU7LRh4i4rCr94DWrbPZ+aFNkgYhVWDjpn/JfbQ+8AfS74bBXXa1VAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1446" height="928"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/13-2.8effe42.1446.png" srcset="/assets/ideal-img/13-2.8effe42.1446.png 1446w" width="1446" height="928"></noscript></div></div></li></ul><p><code>ChatGPT</code> 는 나의 성장 동력에 가속도를 붙여주고 있고 나에게 없어서는 안되는 <code>단짝 친구</code> 가 되었다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="기능부-단합">기능부 단합<a class="hash-link" href="#기능부-단합" title="제목으로 바로 가기">​</a></h3><p><code>블로그</code> 에서는 한 번도 말해본 적이 없지만 내가 <code>코딩</code> 이라는 것을 시작하게 된 계기는 <code>고등학교</code> 시절 <code>기능반</code> 활동을 하면서 이었다.</p><p>당시 <code>C언어</code> 로 로봇을 움직이기 위해 <code>임베디드 펌웨어</code> 를 수정을 하고, <code>미션</code> 을 해결하기 위해서 여러 <code>알고리즘</code> 을 사용해 문제를 해결하는 활동이었다.</p><p><code>졸업</code> 후 시간이 흘러 다들 바빠서 한자리에 모이지 못했는데 기능반 선생님이 <code>은퇴</code> 를 하시게 된다고 하여 정말 오랜만에 모여 기능반 후배도 보고 선생님도 만나 뵐 수 있었다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAKAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABQAD/8QAJBAAAgEDAwMFAAAAAAAAAAAAAQIDBAURABIxBiEiExRBUYL/xAAUAQEAAAAAAAAAAAAAAAAAAAAC/8QAGBEBAAMBAAAAAAAAAAAAAAAAAQACETH/2gAMAwEAAhEDEQA/ADqrqNHuPsbHcq+41kqosL7lG9TnxA2g9h34xga1lkusUrxnqykypK8/X51dB0tPHRJOkESzF2JkCANnbznnQMbt6a+TcD50cLMdRDs//9k=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="886" height="886"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/14.11fc807.886.jpeg" srcset="/assets/ideal-img/14.11fc807.886.jpeg 886w" width="886" height="886"></noscript></div></div><p>나의 <code>코딩</code> 이 첫 시작되었던 계기인 만큼 뜻 기쁜 시간이었다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="나눔셈-프로젝트">나눔셈 프로젝트<a class="hash-link" href="#나눔셈-프로젝트" title="제목으로 바로 가기">​</a></h3><p><code>비영리 회계</code> 프로그램을 만드는 프로젝트로 <code>Prisma</code> , <code>react-hook-form</code> , <code>tRPC</code> , <code>Playwright</code> , <code>jest</code> , <code>레이어드 아키텍처</code> , <code>DDD</code> , <code>트랜잭션 격리 수준</code> , <code>회계</code> 등 다방면의 경험을 할 수 있었다.</p><p>특히 간소하지만 <code>회계</code> 를 배우면서 <code>경제 시스템</code> 이 어떻게 돌아가는지 알게 된 것도 많이 흥미로웠다. <code>도메인 지식</code> 을 얻는 과정은 어렵지만 얻는 수확은 큰 거 같다.</p><p>이와 반면해서 해당 프로젝트를 하면서 <code>더닝 크루거</code> 의 <code>고점</code> 에 가까워졌다고 생각한다.</p><p>간단하다고 하는 <code>CRUD</code> 개발이 어렵다고 느끼는 동시에 <code>모던</code> 하게 개발을 풀어내는 것을 경험하면서 나는 뭐든 할 수 있다는 <code>자만심</code> 에 가득 차 있었다.</p><p>나만의 울타리 안에 갇혀 생각하던 <code>자만심</code> 은 <code>부서 이동</code> 을 하면서 박살 나게 된다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="부서-이동">부서 이동<a class="hash-link" href="#부서-이동" title="제목으로 바로 가기">​</a></h3><p>일련의 상황으로 <code>부서</code> 를 이동하게 되었다.</p><p><code>부서</code> 이동이라는 것을 처음 경험해 보는데 같은 회사라도 <code>부서</code> 이동을 한 것만으로도 새로운 회사에 온 듯한 느낌을 받을 수 있어 신기했다.</p><p><code>부서</code> 이동은 어떤 과정을 통해서 이뤄지는지 좋은 경험을 할 수 있었지만 새로운 <code>문화</code> 와 <code>기술</code> 을 배우느라 정신이 없었다.</p><p>이전에 <code>나눔셈 프로젝트</code> 를 하면서 <code>더닝 크루거</code> 의 <code>고점</code> 에 가까워졌다고 생각했는데 <code>부서</code> 이동을 하면서 <code>하락</code> 하기 시작했다.</p><p>오랫동안 이전 부서에 있으면서 나의 울타리 안에 <code>갇힌 생각</code> 이 드러나면서 부끄러움도 많이 느끼고 막막함을 느끼게 되었는데 슬픈게도 이 상황이 만들어지지 않는 방법을 알았음에도 나는 하고 있다고 착각하고 있었다.</p><p>지속적으로 공부를 해야 했는데 <code>노트 정리</code> 를 통해 해결되는 <code>호기심</code> 이 내 실력이 올라가고 있다고 착각했고, 결과물을 만들어서 나의 실력을 증진, 증명해야 하는데 나는 할 수 있어! 라는 망상으로 말만 하는 개발자가 되었던 거 같다.</p><p>여러 매체를 통해 이러지 않아야 한다는 것을 알면서도 행동하지 못한 내가 미웠지만… 부서 이동을 하지 않았다면 이 울타리가 언제 부서지지 못했을 것이라는 생각에 위안을 가지고 다시 열심히 해보려고 한다.</p><p>돌아보면 이전에는 새로운 프로젝트하고 공부하는 것을 즐겨 했는데 어느 순간부터 <code>메모</code> 와 <code>정리</code> 라는 것에 대해서 너무 직찹하게 된 것이 아닌가 싶다.</p><p>여러모로 여러 생각을 할 수 있게 되었다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="첫-오프라인-컨퍼런스-방문">첫 오프라인 컨퍼런스 방문<a class="hash-link" href="#첫-오프라인-컨퍼런스-방문" title="제목으로 바로 가기">​</a></h3><p>이런 혼란스러운 시기에 <code>2023 인프콘</code> 에 당첨되어 처음으로 <code>오프라인 컨퍼런스</code> 에 방문할 수 있었다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAANAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAABgP/xAAiEAACAgEDBAMAAAAAAAAAAAABAgMEEQAFEgYTFCExUnH/xAAUAQEAAAAAAAAAAAAAAAAAAAAD/8QAGxEBAQEAAgMAAAAAAAAAAAAAAgExAAMhYaH/2gAMAwEAAhEDEQA/AK7F0nthr0++KNmN41fki/ZsZxjBI/dBrlevDbnjjpckR2VWIIJAPzp1fuSbdvS0oi3hwQrKsKkKuARhR69DQm25ktTOOShnYgcicZOlaCMh2b84APYVap4uZ75//9k=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="768" height="1024"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/15.75900ed.768.jpeg" srcset="/assets/ideal-img/15.75900ed.768.jpeg 768w" width="768" height="1024"></noscript></div></div><p>나름의 <code>리프레쉬</code> 할 수 있는 시기가 되었다. 자세한 것은 <a href="/blog/2023/26/08/infcon-2023-retrospective/">인프콘 2023 회고</a>에서 살펴볼 수 있다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="대한민국-남쪽-여행">대한민국 남쪽 여행<a class="hash-link" href="#대한민국-남쪽-여행" title="제목으로 바로 가기">​</a></h3><p>혼란스러운 시기는 여기서 끝나지 않았다.</p><p><code>부서</code> 적응하느라 바쁜데 <code>훈련소</code> 가라고 통지서가 나왔다. 어찌 보면 좋은 기회일 수도 있다고 생각했다. 가서 몸 구르며 생각도 정리할 수 있을 테니</p><p><code>연차</code> 와 <code>복지 포인트</code> 가 남아서 여행을 한 번 더 가야 되긴 하는데 <code>훈련소</code> 수료 이후는 <code>겨울</code> 이라서 이전에 가야 했다. 마침 <code>추석</code> 및 <code>대체 공휴일</code> 이 지정되면서 <code>장기 연휴</code> 가 발생했고 <code>연차</code> 를 기안하면 <code>여행</code> → <code>추석</code> → <code>훈련소</code> 로 아름답게 갈 수 있는 날짜가 되었다.</p><p>드라마틱 하게 <code>훈련소</code> 날짜가 <code>장기 연휴</code> 인 <code>추석</code> 이후라서 가능했다.</p><p><code>복지 포인트</code> 로는 대한민국 구석구석 가보다는 컨셉으로 생각해서 저번에는 <code>제주도</code> 와 <code>동쪽 여행</code> 을 다녀왔으니 이번에는 <code>남쪽 여행</code> 을 가자고 마음먹었다.</p><p>선선한 하니 최고의 날씨이었고 방문한 곳이 사람이 별로 없는 고즈넉한 <code>시골 분위기</code> 가 많아서 잔잔하게 생각 정리하기 좋았다.</p><p>큰, 특별함이 없는 소소한 행복을 할 수 있는 여행이었다. 나중에 힘든 일이 있어서 조용한 곳에 있고 싶을 때 또 방문하고 싶다.</p><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAANAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABAUH/8QAIxAAAgIBAwMFAAAAAAAAAAAAAQIDBBEABQYHI1ESITGBkf/EABQBAQAAAAAAAAAAAAAAAAAAAAL/xAAZEQACAwEAAAAAAAAAAAAAAAABAwAEESH/2gAMAwEAAhEDEQA/AKb8a7QMihMnALe2T96OeJWMnst+awbceVbvapxU579t68J9SI0xIB8jTI+o3Ko41SPeraooAVQVwB4+NNd52dyBlVYPCZ//2Q==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="768" height="1024"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/16-1.99e1079.768.jpeg" srcset="/assets/ideal-img/16-1.99e1079.768.jpeg 768w" width="768" height="1024"></noscript></div></div></td><td><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAIAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAQF/8QAIhAAAQMEAAcAAAAAAAAAAAAAAQIDEQAEBQYHEhMhQUJR/8QAFQEBAQAAAAAAAAAAAAAAAAAAAQT/xAAaEQACAgMAAAAAAAAAAAAAAAAAAQMSFCFx/9oADAMBAAIRAxEAPwCnDb1nbayabyeuvX18AQXrflaDkCRKCSpJ7gTEGa1hxJXA6mr7MlfslLIIB+A+aUqXLk1xBRH/2Q==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1461" height="1169"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/16-2.d23daa8.1461.jpeg" srcset="/assets/ideal-img/16-2.d23daa8.1461.jpeg 1461w" width="1461" height="1169"></noscript></div></div></td></tr></tbody></table><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="훈련소-입대">훈련소 입대<a class="hash-link" href="#훈련소-입대" title="제목으로 바로 가기">​</a></h3><p><code>부서 이동</code> 으로 혼란스러운 시기에 <code>훈련소</code> 라니 나에게 주어진 <code>하프 타임</code> 이라 생각하고 가서 생각 정리할 수 있는 것으로 기대했다. 물론 정리할 수 있도록 편한 환경은 아니었지만 말이다.</p><p><code>훈련소</code> 라니… 나에게 멀다고 느껴지면서도 실제로 가게 되는 날이 오니까 신기했다.</p><p><code>군대</code> 라는 곳은 가보면 어떨까? 매체를 통해 많이 접했지만 실제로 가서 느끼는 것은 어떻게 다를까? 이런 호기심, 설렘, 걱정을 가지고 입대하게 되었다. 그 덕에 <code>10월</code> 은 딱히 한 것이 없다.</p><p>가서 <code>군대</code> 라는 시스템에 대해서 알게 된 것도 있고, 여러 사람을 만나며 다양한 생각을 접할 수 있었다.</p><p>여기서도 나의 생각의 울타리를 부시기 위해서 많이 물어보고, 배우는 느낌으로 접근했다. 이런 것들은 꼬박꼬박 <code>일기</code> 로 작성했는데 시간이 지나서 읽으면 어떤 느낌일까? ㅋㅋㅋㅋㅋ기대된다.</p><p>훈련도 다 참여하고 안전하게 <code>수료</code> 할 수 있었다!</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="도봉산으로-가을-등산">도봉산으로 가을 등산<a class="hash-link" href="#도봉산으로-가을-등산" title="제목으로 바로 가기">​</a></h3><p>훈련소에 있는데 등산이 그렇게 가고 싶더라</p><p><code>2023</code> 년에 <code>북한산</code> 을 등산한 기억에 좋게 남아서 <code>2024</code> 년에도 <code>도봉산</code> 으로 등산을 떠났다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAANAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABgIF/8QAJBAAAQMDBAEFAAAAAAAAAAAAAQIDBAUREgAGE1EHIjGx0eH/xAAUAQEAAAAAAAAAAAAAAAAAAAAE/8QAGREAAgMBAAAAAAAAAAAAAAAAAAEEEUEU/9oADAMBAAIRAxEAPwBU1u6nRHEhx9odYqJPxrZT5FpYSByue3R+tA6Ftin1ZiBLltpSmSXQtllPGkYJGJFv3VK8XwMjxz5KUX9KcEGw6vbSey8DKMlp/9k=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="768" height="1024"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/17.6f4d87f.768.jpeg" srcset="/assets/ideal-img/17.6f4d87f.768.jpeg 768w" width="768" height="1024"></noscript></div></div><p>앞으로 매년 한 번씩 등산가는 <code>챌린지</code> 를 하려고 한다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="코딩-테스트-공부-시작">코딩 테스트 공부 시작<a class="hash-link" href="#코딩-테스트-공부-시작" title="제목으로 바로 가기">​</a></h3><p><code>고등학교</code> 시절 <code>기능반</code> 의 문제 해결을 위해서 <code>알고리즘</code> 을 공부하고 사용했어서 <code>알고리즘</code> 에는 자신이 있었다.</p><p>물론 이것도 나만의 <code>자만심</code> 이었다.</p><p>정신 차리고 <code>코딩 테스트</code> 를 공부를 시작했다. 늦은 감이 있지만 이전 기억을 살려서 잘 해보려고 한다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="공부-시간-확보-노력">공부 시간 확보 노력<a class="hash-link" href="#공부-시간-확보-노력" title="제목으로 바로 가기">​</a></h3><p>생산적인 활동을 하기 위해서 시간 배분을 하는데 현실적으로 직장인에게 주어진 시간은 정말 짧더라</p><p>내가 어디까지 시간을 당겨쓸 수 있는지 여러 테스트를 해보았는데 기억에 남는 것으로 <code>하루에 4시간</code> 수면을 해보았다.</p><p>사실 <code>4시간</code> 수면이라는 목표는 없었는데 공부를 하다보니 늦게 자게되어 <code>4시간</code> 수면이라는 결과가 나왔다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAMCAYAAABbayygAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABLElEQVQYlV2Ry04cMRBF+///giwQShCwidhGyiCxHgSDWAQpo8yjgZm22+W3fSL3MAhRG/uWb50rlTu+lDEGpdSHjhW2BroYI8MwICJoPWKtJQSPcw7vPX3/gnOeLuc8UUIIiLUs11tW/zYsl6vJ0CAN1n2OrcDD/RN38wWz2S0+xI+3rtZKyoFc0uRUZmSvFN5HSowUEVLKB+Kj0zzJK7swsg17XD6QUikYEUp+N/4S4XQYuBHDzAp3xrBRipVS7L2nlkJHrfwW4WQY+KE1Z1pzvnvjZ99ztd3wbAxMRmDuLBfDC2f7nmv1xqVWfB8137Ri4Rwco/8Ezy5a1kF4TY6FUvzte+brNaMRyjE6ltKOqUoFm9oGjo1CSulAbBNNpJyne/uE/N5ruq3wP0frzMt2j5SUAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1170" height="1431"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/18.36b4829.1170.png" srcset="/assets/ideal-img/18.36b4829.1170.png 1170w" width="1170" height="1431"></noscript></div></div><p>더 젊을 때는 했던 거 같은데 이제는 많이 힘드네? 를 몸으로 직접 체험할 수 있었다.</p><p>무작성 시간을 확보한다고 해서 공부가 되는 것은 아닌 거 같고 나의 라이프 스타일에 맞는 공부 시간을 찾기 위한 노력이 더 필요해 보인다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="바른-자세란">바른 자세란<a class="hash-link" href="#바른-자세란" title="제목으로 바로 가기">​</a></h3><p>엎친 데 덮친 격으로 이제 의자에 앉아있는 자세도 불편하더라 알고 보니 바른 자세가 아니었는데 다리가 땅바닥에 닿기 위해서 의자를 내리니 어깨가 위로 들려 아픈 것이었다.</p><p>바른 자세를 알고 나니까 <code>모션 데스크</code> 의 필요성을 느끼게 되었다…</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="기타">기타<a class="hash-link" href="#기타" title="제목으로 바로 가기">​</a></h2><ul><li>2023년에 좋아하는 많은 노래를 건질 수 있었지만 정말 내 마음을 울리는 노래를 건져서 좋았다. 나중에 다시 이 감정을 느끼고 싶어서 기록으로 남긴다.<ul><li>STAYC(스테이씨) - Teddy Bear</li><li>미연 ((여자)아이들) - Drive</li><li>TOIL, Gist - 끝말잇기 (Feat. Skinny Brown)</li></ul></li><li><code>해피 해킹</code> 과 <code>에어팟 맥스</code> 가 사고 싶어졌다…</li><li><code>2023-03-15</code> 부터 <code>GitHub Repo Release</code> 알림을 끄기 시작했다.<ul><li>원래는 아래와 같이 관심 있는 것들에 대해서 알림을 활성 해 놓았는데.<div class="imgContainer_g3cu spacer_xInT" style="width:40%"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAALCAYAAABGbhwYAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABC0lEQVQYlXWQ2U7DMBBF+wmopE2zekni7EnTllBohJD4DV74/484yO7CEw8jj32P7p3xKm8m5uWLw+sH4+lCmtXocqDqT7T7mVTX+JFmleiKepydIIqWIC0I04JIlljN3reRYuUFkmp4oT++38SGWNekeUuSNeySHG8nLCiIVU2sKpQZ0OWIMr2LV2ZwjtbMOVpI5C2bULnHbaRdvwnVrbfgTqBNSzPOZNXo4v+A6/lwrKaFdjpjuiOhMP+Dpp/ppjN5vX+IrmLNNsnwXHQgUHnjorvDm1ti7ac8BwLPk6yfUjzfbS3dF9hIO5/dUpqOvJyQnwPdzwW/zK7gfY57ycKCe8TS0X8v+CbjFycqmiGrZxl9AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="315" height="351"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/19.1ac894c.315.png" srcset="/assets/ideal-img/19.1ac894c.315.png 315w" width="315" height="351"></noscript></div></div></li><li>알림이 너무 맨날 쏟아져서 정말 중요한 메일을 분류하기가 힘들어졌다. 맨날 읽고 <code>패치 노트</code> 를 쫓아갈 수 있다고 생각했지만 현실은 그렇지 않았다.<div class="imgContainer_g3cu spacer_xInT" style="width:40%"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAALCAYAAABGbhwYAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAA4ElEQVQYlV2Q246DMAxE+f8vXO1SWrpcnGAaAuTS7EpTOa1o1IeJZOtkPHbF8wLSjHP7i5tZ4UOC8/csHxJ2F7HtAZVZdgg8kMa6BcT0f0D+9UngKsQ/KD2D2aA5t6CRIb3SVVRJYVeXNbHBsuzwL9AXMSopiBjDSDmnUvwBpaejPJJtsXt2tNbB+ViMLhxHYijSaE4tTnWLa9thmAO27FaAkk8cjd3yiaQ223uZA5StR1LoesJP3aAfFBqKWN0HKLc0xuaMxjqwTQjxfcdjGT3d0PU9vr5rXDqF6/QcW+oBOmGk4buHH3sAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="196" height="211"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/20.7c47467.196.png" srcset="/assets/ideal-img/20.7c47467.196.png 196w" width="196" height="211"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT" style="width:40%"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAZCAYAAAAIcL+IAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABx0lEQVQ4jWVTi46EIAz0/z9zd10B8QXIU5y7ltPby5E0mDBOO9O2c9bA7BEpF+SUkHLGeZ6otXLQN0VXK5BzRkoJpZQbQN+5FL7prdtDwqt/o3/1MMbAWot937FtG2KM/FPwAV3MBcu6YlkWfqDIubGUUnCewLqu6ELMGITAu++ZzXvP4OsQUEqJLqQMIRWGYeCUIQSOTyFKKnQxFUg1QgrRgN7D/wECSiliLBiEZEZj7F0nAVtuSs3AjLeQmKaJGZ1zsNbdtdZ6NmBKGaOesG0rAy/wp6hxHJvqfhDQWnNqYw22zdzgk8RQjbkcWDcD73f27TOO42iMihlbZ6QUbDrFPM/cGeccM3KNuVS2o5l7/gs69CP3ehAKyzJzZ4y13L7rUPoQYjP8PQg8nw8GXj5Sjb/A0FQ/ni+ukaaH1YZwCzkuoP+xh0QQiHwkb68aP4AJctS34W5vNv3WWFuNxPh49hBiYMbWwja8JIoZaXBpDa520U1p6Ka41PMqxHzwPBqz/dmd65RywBMjqaZ5pNTUb2IkdkpJo3Yw0KMrR4VUiieEgDElWOfacv2kpm3s6HHSGlpPeAsNLSXHpBS0UpjHEfP3UHwBJMTHx8uA2WgAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="482" height="1217"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/21.14cb209.482.png" srcset="/assets/ideal-img/21.14cb209.482.png 482w" width="482" height="1217"></noscript></div></div></li></ul></li><li>이외 공부했다고 할만한 것은?<ul><li>올바른 테스트란?</li><li>Next.js 13 폰트 최적화</li><li>tRPC</li><li>트랜잭션 격리 수준</li><li>VPN, TLS</li><li>레이어드 아키텍처</li><li>PHP Docker 환경 구축</li><li>Mysql Index 원리</li><li>SVN</li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="돌아보면">돌아보면<a class="hash-link" href="#돌아보면" title="제목으로 바로 가기">​</a></h2><p><code>소프트웨어 엔지니어</code> 의 삶과 이외로 <code>여행</code> 도 다니고, <code>운동</code> 의 한계도 극복하고 성장한 <code>나</code> 가 될 수 있었지만<br>
<code>Impact</code> 있는 행위는 없었고 나를 <code>객관적</code> 이게 되돌아보고, 부족한 점을 깨닫고, 앞으로 나아갈 방향을 더욱 구체화할 수 있었던 한 해라고 생각한다.</p><p>앞으로 나아가기 위한 발판이랄까?</p><p>아래의 것을 늘 지키느라 나에게 <code>저녁</code> 은 늘 편안하게 잘 수 있었던 적은 없었다.</p><ul><li><code>데일리 Commit</code> 는 했는지</li><li><code>영어 공부</code> 는 했는지</li><li><code>코딩 테스트</code> 는 풀었는지</li><li><code>주식</code> 은 잘 되는지</li><li><code>메일</code> 은 읽었는지</li><li><code>PKM</code> 에서 <code>정리</code> 안된 것은 있는지</li><li><code>미리 알림</code> 에서 누락된 <code>Todo</code> 는 없는지</li></ul><p>이런 것을 모두 확인하고 잘 되어야지 하루를 문제없이 잘 보냈다고 생각하기에 그래왔지만 슬슬 한계가 오는 거 같다. 과연 이게 지속 가능성이 있는 걸까?</p><p>한편으로는 너무 피곤하게 사는 건가 싶으면서도 이렇게 하지 않고 다른 방법이 있는가 싶었다. 그럼에도 잘 수행한 나에게 칭찬은 해주고 싶다.</p><p><code>더닝 크루거</code> 곡선의 <code>자신감 하락</code> 이 시작되었지만 <code>좋은 평가</code> 도 들었기에 문제를 파악하고 개선하면 된다고 생각한다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="앞으로는">앞으로는?<a class="hash-link" href="#앞으로는" title="제목으로 바로 가기">​</a></h2><p>문제점은 명확했다. 너무 <code>메모</code> 와 <code>정리</code> 에 집착했고 나는 이것을 내려놓으려고 한다.</p><p><code>호기심</code> 해결이 아닌 <code>공부</code> 를 하고, <code>생각</code> 만 하지 말고 <code>구현체</code> 로 내 실력을 증명하려고 한다. (책도 많이 읽고)</p><p>늘, 미래를 기대하는 <code>회고</code> 로 마무리가 되었는데 이번에는 바뀔 점이 가득하구나<br>
<!-- -->하지만, 걱정은 없다. 나는 늘 잘 해왔으니까 내년의 나는 문제를 개선해서 웃고 있기를 바라며 <code>회고</code> 를 마무리 하곘다.</p><p>읽어주셔서 감사합니다.</p>]]></content>
        <category label="개인 회고" term="개인 회고"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[PhpStorm, VSCode에서 실행 가능한 PHP Docker 개발 환경]]></title>
        <id>/2023/09/05/php-docker-dev-env-phpstorm-vscode</id>
        <link href="https://parkgang.github.io/blog/2023/09/05/php-docker-dev-env-phpstorm-vscode"/>
        <updated>2023-09-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[PHP로 된 회사 제품 중 Debug 환경이 없어서 일일이 echo 으로 값을 찍으면서 디버깅을 하였다…]]></summary>
        <content type="html"><![CDATA[<p>PHP로 된 회사 제품 중 Debug 환경이 없어서 일일이 <code>echo</code> 으로 값을 찍으면서 디버깅을 하였다…</p><p>이 큰 코드 베이스를 이렇게 Debug 하는 것은 아니다 싶었다.</p><p>JetBrains의 PhpStorm을 사용하고 있는데 뭔가 방법이 없을까? 그동안 Debug 환경이 당연한 곳에서 하다가 만난 새로운 난관이었다.</p><p>여러 삽질을 하여 만든 Docker PHP 환경을 공유한다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="php-개발-환경-insight">PHP 개발 환경 Insight<a class="hash-link" href="#php-개발-환경-insight" title="제목으로 바로 가기">​</a></h2><p>PHP 개발 환경은 내가 경험한 보편적인 개발 환경과 많이 달랐다. 이것에 대해 먼저 알고 넘어가는 것이 좋을 것 같다.</p><ul><li>언어 기능이 빈약하거나 모든 기능 사용을 위해서는 유료다.<ul><li>알게 모르게 우리는 자동 완성, 참조 추적, 리팩터링과 같은 언어 기능을 사용하고 있다.</li><li>언어 개발사에서 제공하거나 IDE를 개발한 곳에서 언어 기능을 제공하거나 오픈 소스로 개발되어 잘 사용하고 있던 것이다.</li><li>그런데… PHP 언어 기능은 시중에 <code>PHP Intelephense</code> 가 나은데, 리팩터링 같은 기능을 쓰려면 유료 라이선스 사용이 필요하다.</li><li>PhpStorm도 유료이니… 사실상 실무 제품을 개발하려면 유료다.</li></ul></li><li>언어 기능과 Debug 기능 따로 설치<ul><li>언어 기능과 Debug 기능이 분리되어 있을 순 있다. Go의 Debug는 <a href="https://github.com/go-delve/delve" target="_blank" rel="noopener noreferrer">delve</a>로 존재한다.</li><li>보통은 SDK 설치하면 한번에 딸려오고 손쉽게 설정할 수 있는데 PHP는 따로 설치하고, 소켓 통신을 위해서 설정 건드려야 하는 부분도 있었다.</li><li>모던 버전이 아니라 그런 것일 수 있겠지만 처음해보는 경험이었다.</li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="docker-php-개발-환경-사용-방법">Docker PHP 개발 환경 사용 방법<a class="hash-link" href="#docker-php-개발-환경-사용-방법" title="제목으로 바로 가기">​</a></h2><p>아래와 같이 Sample 코드를 만들어놓았다. 버전마다 차이가 있어서 모던 <code>PHP@8</code> 과 레거시 <code>PHP@5</code> 를 만들어놓았다. 자세한 실행 방법은 README.md 를 읽으면 되며 VSCode, PhpStorm 모두 언어 기능+디버깅을 사용할 수 있다!</p><ul><li>PHP@8: <a href="https://github.com/parkgang/concept-container/tree/main/php/dev-env/php-8-xdebug-on-docker" target="_blank" rel="noopener noreferrer">https://github.com/parkgang/concept-container/tree/main/php/dev-env/php-8-xdebug-on-docker</a></li><li>PHP@5: <a href="https://github.com/parkgang/concept-container/tree/main/php/dev-env/php-5-xdebug-on-docker" target="_blank" rel="noopener noreferrer">https://github.com/parkgang/concept-container/tree/main/php/dev-env/php-5-xdebug-on-docker</a></li></ul><p>만약, VSCode <code>PHP Intelephense</code> 를 쓴다면 <a href="https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client" target="_blank" rel="noopener noreferrer">확장 공식 문서</a>에 작성된 대로 따라하면 된다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAgklEQVQImT1OCQ7DIAzj/x+tNGBc5T5cOdWGZCUhjm1lrYX3Hr13tNYE5xz8HntCcXDO4bou5Jwx58RaS7D3/h8pLrTWMMaIckpJKhFjlJkcVWuVD1qWnAVUvu9b6hjjtSaBOY3R+BiLb0gSxYcAipRShKx4JQvv4WiXXiUSaMmcxAP3OuiHsHJl8wAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1466" height="854"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.e21ba0a.1466.png" srcset="/assets/ideal-img/1.e21ba0a.1466.png 1466w" width="1466" height="854"></noscript></div></div><p>친절하게 설명하자면 아래와 같다.</p><ul><li><code>VSCode</code> 의 기본 제공 기능인 <code>PHP Language Features</code> 를 비활성화<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAs0lEQVQImVWOsW7CMABE7YCd2HFiJySGNI5KIEXqAmKqylb1/7/pVW0ZYDjdDU9PJ9o4EvoBITVSWzJtkeq/ta0x1QbXbBHDNLNLB5o4YqqWvAyYeoMuPco8gOfLlbQ/MkwHfLdDW0/hAqu8ZF04rO/uxrcP0nImzQtpPtFuR3w3UIaOJr7cd0QoF9GuR6oCqczft1+bUIYq9CynC9PrO0Jka0SmEKv8OVLh6pbv2xef1xs//vZH1zJlb28AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1320" height="750"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.b3385f3.1320.png" srcset="/assets/ideal-img/2.b3385f3.1320.png 1320w" width="1320" height="750"></noscript></div></div></li><li><code>settings.json</code> 에 아래의 내용을 추가<div class="language-json codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-json codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"files.associations"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"*.module"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"php"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="만들면서-경험한-tmi">만들면서 경험한 TMI<a class="hash-link" href="#만들면서-경험한-tmi" title="제목으로 바로 가기">​</a></h2><p>이 환경을 만들면서 별의 별 삽집을 다 해보았다. 정리되지 않은 TMI 내용은 더 있지만 공유될만한 내용을 공유해본다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="php-개발-환경-요약">PHP 개발 환경 요약<a class="hash-link" href="#php-개발-환경-요약" title="제목으로 바로 가기">​</a></h3><ul><li><code>PHP</code> 에서 <code>Debug</code> 를 위해서는 <code>XDebug</code> 를 설치해야한다.</li><li><code>php -m</code> , <code>phpinfo();</code> 를 통해서 설치 여부, 설정 값을 확인할 수 있다.</li><li><code>XDebug</code> 와 <code>소켓</code> 통신을 통해서 <code>Debug</code> 정보를 주고 받는다.</li><li><code>XDebug 버전 3</code> 의 경우 <code>9003</code> port, <code>XDebug 버전 2</code> 의 경우 <code>9000</code> port 를 기본 값으로 사용한다.</li><li><code>VSCode</code> , <code>PhpStorm</code> 모두 <code>Xdebug</code> 의 정보를 받을 수 있도록 <code>listen</code> 중이고 <code>Xdebug</code> 가 <code>로컬 머신</code> 으로 정보를 보내면 그에 맞게 <code>Breaking Point</code> 가 걸리는 것이다.<ul><li>이 <strong>대전제</strong>를 두고 <code>Docker</code> 실행 환경일 때, <code>Remote</code> 실행 환경일 때 동작하는 것이다.</li><li>그래서 <code>VSCode</code> 에서 <code>F5</code> 으로 <code>Xdebug</code> 실행 중이거나 <code>PhpStorm</code> 이 실행된 상태에서 <code>lsof -i :9000</code> 명령을 통해 <code>listen</code> 중이라는 것을 확인할 수 있다.</li></ul></li><li><code>Docker</code> 까지는 <code>HOST OS</code> 위에서 돌아가기 때문에 <code>로컬 머신</code> 과 통신이 어렵지 않지만 <code>Remote</code> 환경의 경우 말이 달라진다. <code>공인 IP</code> 를 사용하는 <code>로컬 머신</code> 에 통신 하려면 귀찮은 과정이 필요하다. 이런 경우 <code>SSH 터널링</code> 을 이용해서 손쉽게 <code>로컬 머신</code> 에 통신할 수 있으니 <code>Remote</code> 환경에서 <code>Debug</code> 이 필요한 경우 <code>SSH</code> 터널링을 사용하면 된다.</li></ul><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="vscode-php-언어-기능-종류-조사">VSCode PHP 언어 기능 종류 조사<a class="hash-link" href="#vscode-php-언어-기능-종류-조사" title="제목으로 바로 가기">​</a></h3><ul><li>기본적으로 <code>VSCode</code> 에서 <code>PHP</code> 관련 확장을 지원합니다. 하지만 그럼에도 기본 기능이 너무 빈약합니다. <code>변수 인텔리센스</code> (되는거 같아 보이지만 선언된 변수만 나오는게 아니라 이전에 입력된 것들 하이라이팅 해주는 것 정도) , <code>참조</code> 기능조차 지원되지 않습니다.<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAjElEQVQImW3LsQ6CMBSF4aIgTe2ltNTaCi0QYXRwMibO6Pu/0DEyuOjw5V/OYaQ9TmnGp1khkXOFndA/GNVHhDghpBmHMMC4+H+4LE/c7g80roVvexjrUZQSsmpWpCy4qMCksqDKQFuP2rgVqQbZtsQm51/Mj1fEywtdGtGmM7p+ggsRgjT2ZMCFWk9vDoM86qz8h2cAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1130" height="636"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.2b6f66d.1130.png" srcset="/assets/ideal-img/3.2b6f66d.1130.png 1130w" width="1130" height="636"></noscript></div></div></li><li>이외 <code>PHP</code> 언어 서버 관련 확장은 아래 2개가 있었으며 특징은 아래와 같습니다.<ul><li><a href="https://marketplace.visualstudio.com/items?itemName=zobo.php-intellisense" target="_blank" rel="noopener noreferrer">PHP IntelliSense</a><ul><li><code>PHP</code> 공식 확장 같아 보이긴 하지만 이상하게 <code>PHP Intelephense</code> 보다 압도적으로 다운로드수가 적더군요.</li><li>해당 확장을 사용하면 <code>참조</code> 추적 같은 것은 됩니다.</li><li>하지만<ul><li><code>참조</code> 추적에서도 <code>정의</code> 된 부분까지는 추적으로 안나오고 <code>정의</code> 를 호출해야지 추적이 됩니다. (<code>참조</code> 추적에서 <code>정의</code> 부분과 함께 못 본다는 아쉬움)</li><li><code>변수 인텔리센스</code> 가 구립니다. <code>빌트인 PHP</code> 와 다를 것이 없습니다.</li><li><code>기호</code> , <code>정의</code> 살펴보기가 안됩니다. (근데 확장 README에 보면 되는거 같긴 한데 이건 <code>PHP@8</code> 이라서 그런거 같기도 한데 최신 버전이라서 안되는 기능이 있다는 것이 자체가 아쉽습니다.)<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAgElEQVQImXWOywrCMBRE8wHV5mEet0ZvbIqJC7sV///DRlJBIejiMDAchhE+LojpCkoVOiQoz9AdrRMmMOZ8Q+AK5U4YpMNO+Q4HIQ3hMhdwrpjOC+Rh+sgt33gIaSPy/YmyPjZpNPQToewRxAXtwjDabunLJlpiaBex1+HPR48XfqJLmO+Sa18AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2038" height="1286"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.193f748.2038.png" srcset="/assets/ideal-img/4.193f748.2038.png 2038w" width="2038" height="1286"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAXElEQVQImXWMSQqAMBAEJ4shmyYgEZeT/39kS8agSPBQTE9TNPlxxnqckNp3CO0YZQJI2wlp3piQC2JeENJ9c9k5i6GJ9ZEmguoKl/6TH5GkBSn3yyvWoq10NPEC7Ho2h2dZejMAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="790" height="442"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.c4512d7.790.png" srcset="/assets/ideal-img/5.c4512d7.790.png 790w" width="790" height="442"></noscript></div></div></li><li><code>호버</code> 시 정보가 아쉽습니다. <code>PhpStorm</code> 은 관련 링크 및 자세한 설명이 있는데 말이죠<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAABYlAAAWJQFJUiTwAAAATUlEQVQImQXBWQqAIBRAUdfQZII+p8SBiD6K9r+y2zlq3iw1VlzohNhI5STmjuSB8Q3xBesyajaBNB7O+2NcLz4PJDWMO5i0sO7Coh0/X40bzRTHKeQAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1560" height="294"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.13d57dd.1560.png" srcset="/assets/ideal-img/6.13d57dd.1560.png 1560w" width="1560" height="294"></noscript></div></div></li><li>중복 변수 선언에 대해서 경고하지 않습니다.<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAvUlEQVQImSXKu1KDQABAURp1DM99wL4IsASzkEwYtVLHlP7/J12LVKc52XNlyYWnFoaibnl6KVH9SlwupPXGOd3wfSR7FQEREtZP2P6EMkf8tBLiynHecMMbhTBkpbQ0ZsS5gW1aEG1AaE8lLbV6WDQdmWwDlTQ03YD0MzrM2HhBuYlDqThUirzSZHnd0miPdJF23OiGhIsbfr4i7UjXL+hwekRlerpxZdrvzPsP549frl9/jOmd9Hln2b/5B9hgTvo+w68DAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="808" height="502"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.564bb98.808.png" srcset="/assets/ideal-img/7.564bb98.808.png 808w" width="808" height="502"></noscript></div></div></li><li>무엇보다 <code>README</code> 에 보면 <code>PHP@7.4</code> 이하에서는 동작하지 않는다고 하니 레거시 프로젝트 사용에 적합하지 않습니다.</li></ul></li></ul></li><li><a href="https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client" target="_blank" rel="noopener noreferrer">PHP Intelephense</a><ul><li><code>PHP IntelliSense</code> 보다 앞도적으로 많은 다운로드가 있는 확장입니다.</li><li>해당 확장은<ul><li><code>참조</code> 추적에서 <code>정의</code> 와 함께 나와서 보기도 편하고</li><li><code>변수</code> 관련 인텐리센스도 똑똑하게 되며</li><li><code>기호</code> , <code>정의</code> 살펴보기도 되므로 <code>PHP</code> 확장 중 가장 스마트합니다.<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAiUlEQVQImWXMyQqDMBRG4ew7JZpJo7FOWAPd+P7vdopWWmwXH1y4P0e4qqfrHwxpwVYjuujQ5ZcJ/UZkLpLmJ2NaMGVL7iLaN+S77XYRoXRJ202EZsSFlmvmOd8MZ2kPhDQBf5+JQyJ39XsoHRd1JDJbYeOEcg2nvbQ+/orKBLSvkbr4DH5ra/EFKF5ML70cH2YAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2052" height="1368"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.f265c84.2052.png" srcset="/assets/ideal-img/8.f265c84.2052.png 2052w" width="2052" height="1368"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAc0lEQVQImT3MQQ6CMBQA0Q8trUVpiSBRg5IoamNk4/3vNsZGu5jdy0jddJzOV6zzGOcp1AoRQ6nrnDJrRNuGIQRiXIivN8fxQtjuEyiUS6nqB3vvud2fzI+Ffhhpu0O+Zai+xzYwTTObsENKi0hFqV3qDz+xqS4wtqNaWgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="790" height="346"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9.b77ce51.790.png" srcset="/assets/ideal-img/9.b77ce51.790.png 790w" width="790" height="346"></noscript></div></div></li><li><code>호버</code> 시 <code>PhpStorm</code> 처럼 디테일하게 나오고<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAvElEQVQImSWOQW6DMAAE/YM2CXGNizHGBkHsmESBpipSq9z6/w9NFXoYzWVWWlHoFp8/GfNM0yeK0nIsG/bSsJMVO2nYywpxVDVDnzBuxIWI6xJNiKi6Q1UBXXnku0MUypIvd9afXy7zypBuxGnBj1fqdqQNEW07xEHVzF8PPtYHY15wfcIPE37Im8PpfyBeC835tjIt3wz5TogzfVroTtctetqGiHg5lFRt5M1Gyiah3RntIsoElPEbz49/qmBVvDvWx6sAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1720" height="934"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/10.6522a8e.1720.png" srcset="/assets/ideal-img/10.6522a8e.1720.png 1720w" width="1720" height="934"></noscript></div></div></li><li><code>README</code> 설명 자체에도 <code>PhpStorm</code> 의 메타데이터를 읽는다고 나와있습니다.<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAALElEQVQImR3GsQkAQAgDwN9/wzQJKFaSGfLgVfd2NwBSVSF57+7MzF1SbOcD5iskVTLTLs4AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="407" height="29"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/11.46ea549.407.png" srcset="/assets/ideal-img/11.46ea549.407.png 407w" width="407" height="29"></noscript></div></div></li><li>중복 변수 선언의 경우 린트로 경고해줍니다.<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAvklEQVQImRXN3U6DMACAUW6Mykb5KwVKW1pgAWUmgywhRuP7v9VnuDjXJ3oRLbFQiLzm9T3j7VJg/cqn9dz7gdl6rAlE13pE2gXrJoybkNpjxhVjB1x/Q6qOJK+JRKnJlEPXhtAFUqnJZIsoT5okb7hmiugs07JBqJ6sDUg9oIcVUdTESUmcFFzSikgUDXnVUbkFfdtw84Zfdvy8ocyEW3akDmfdUiiDch/0XweP/Ztj/+V4/hHGO9P2g11W/gHYOk9MsPOUbQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="834" height="490"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/12.9d4d759.834.png" srcset="/assets/ideal-img/12.9d4d759.834.png 834w" width="834" height="490"></noscript></div></div></li></ul></li></ul></li></ul></li><li>결론적으로 가장 똑똑하고 기능이 좋은 <code>PHP Intelephense</code> 확장을 사용하도록 합니다.<ul><li><code>PHP Intelephense</code> 확장을 사용하기 위해서는 확장 <a href="https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client#quick-start" target="_blank" rel="noopener noreferrer">README</a> 에 있는대로 하면 됩니다.</li><li><code>PHP</code> 프로젝트를 열면 <code>PHP</code> bin 등록하라고 경고가 나오는데 <code>PHP Intelephense</code> 를 사용하면서 <code>PHP Language Features</code> 를 비활성화 하므로 <code>bin</code> 등록도 해야 좋은거 아닌가? 라는 생각은 하지 않아도 될 거 같습니다.<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAIAAAA4WjmaAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAcklEQVQImU3GMRKCMBAF0BxDx+x3IJARk/1MnARBLByFwsIbeAfv39lY+KpnAIiI/fmLCLA3IqjbTpmo1BB6JSNj1M63vnEGu61v3Hgul3waNUzUfDywT35+VlrMJkxIN1eWeljcsFb5IbzavNrXB/f3FzLpE8b4gPfrAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="662" height="278"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/13.b1497fd.662.png" srcset="/assets/ideal-img/13.b1497fd.662.png 662w" width="662" height="278"></noscript></div></div></li><li><code>PHP</code> 버전에 맞게 <code>PHP Intelephense</code> 설정 변경 및 <code>라이브러리</code> 사용 시 <code>stub</code> 추가할 것이 있는지 찾아서 설명하면 됩니다.</li></ul></li></ul><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="vscode-php-환경의-아쉬운-기능들">VSCode PHP 환경의 아쉬운 기능들<a class="hash-link" href="#vscode-php-환경의-아쉬운-기능들" title="제목으로 바로 가기">​</a></h3><p>그동안 <code>VSCode</code> 에서 매우 친화적인 <code>TS</code> + <code>Node.js</code> 를 사용하느라 못 느낀 것이지만 <code>VSCode</code> 에서 <code>PHP</code> 을 하려고 보니까 기능이 너무 빈약한 것을 느끼고 있습니다.</p><ul><li>가볍게 편집하고 이런 것은 편해 보이는데, 확실히 <code>리팩터링</code> 및 더 고급 기능을 위해서는 <code>PhpStorm</code>을 쓰는 것이 맞아 보입니다.</li><li>PHP 확장 중 유료 <code>PHP Intelephense</code> 말고는 <code>F2</code>로 리팩토링 기능이 없습니다… 세상에…</li><li><code>JS</code>와 <code>PHP</code>가 합쳐지는 것에 대해서는 참조 기능이 빈약합니다. <code>PHP</code> 확장자이더라도 <code>HTML</code>, <code>JS</code>를 작성할 수 있는데, <code>JS</code>에서 참조 조회 시 <code>VSCode</code>는 <code>JS</code> 파일에 대해서만 생각하고 찾는 것 같습니다.<ul><li><code>PhpStorm</code>은 나오는데<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAV0lEQVQImW3FOw6AIBAFQM6DlZLYKIvCgh/AwmDh/Q/yjKGxsJiMUKqFsQFuu0BLwcAndKh/iUZKeLY4yo3JZ5CLMJxg+L0ijhB932FfLVJOIDNj1PTrAbn/NS+xRaCKAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="825" height="302"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/14.eb2ee8d.825.png" srcset="/assets/ideal-img/14.eb2ee8d.825.png 825w" width="825" height="302"></noscript></div></div></li><li><code>VSCode</code>는 안 나옵니다.<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAb0lEQVQImQXBWQrCMABAwRxARAykizUJ2WyIbaWLRcE/8f43es4IKWtUa1D1FZ0epOmFKxtheGLzTBg2lOkR0RX26c13XPjNH5b7SvKFeJtwIRPHHeN6RNN4nNZ4H9AmUqkLnY1Yn5Ftx+GkOJ4r/kjbKrpoj9ruAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="415" height="123"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/15.e27547b.415.png" srcset="/assets/ideal-img/15.e27547b.415.png 415w" width="415" height="123"></noscript></div></div></li></ul></li><li>다른 확장도 그런지 모르겠지만, 정의되지 않은 변수에 대해서 경고를 표시하지 않습니다. (이건 조금 찾아보니까 LSP가 아니라 정적 도구를 통해서 해결해야 될 것 같더라고요)<ul><li>아래와 같이 정의되지 않은 변수가 표시되어야 할 것 같지만<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAmElEQVQImVXKSw6CMAAAUc4iGKFEI4jGoNKWr9BQCzFuvP85xkQWxsXLbMZLsxPJLiYMN6yC4MsP1vjrf16aHdnvtshrQXsfqJuWqq6RSiN1idQapRVecjgSRRH59cb0ejO6Gesc5jExuCejmzDWLqMQgvxyw84vejvTGUvVDVRd/23Z9ssYC0F2OqPaEdkYVN1RqIpC/3wAzJpVCwvNJ6QAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="486" height="239"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/16.fd505d1.486.png" srcset="/assets/ideal-img/16.fd505d1.486.png 486w" width="486" height="239"></noscript></div></div></li><li>안나옵니다.<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAc0lEQVQImRXK0QqCMBiA0b1Akbo1p2P/pAmhBXaRN1mDwqvq/R/ni871UTvdsClqjq0w+g4nPS4k5DAgaSCmEeMEVRjPtrToytE6Yb49ma4L99ebvH7I65fYn1B7F/lnf15IlwfTnCmNR9dCZQPaBmzT8QPsoipqHPcjjAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="257" height="77"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/17.300940e.257.png" srcset="/assets/ideal-img/17.300940e.257.png 257w" width="257" height="77"></noscript></div></div></li></ul></li><li><code>PhpStorm</code> 은 폴더 이름 변경 시 <code>include</code> 경로도 찾아서 바꿔줍니다… 대박</li></ul><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="vscode-codeigniter-설정">VSCode CodeIgniter 설정<a class="hash-link" href="#vscode-codeigniter-설정" title="제목으로 바로 가기">​</a></h3><ul><li><code>VSCode</code> 에서 <code>PHP Intelephense</code> 설정 후 CodeIgniter을 사용하는 제품을 열었더니 <code>this-&gt;Model</code> 으로 잡는 변수들에서 정의되지 않았다고 오류가 발생했습니다.</li><li>관련해서 찾아보니<ul><li>정의되지 않은 것에 대한 경고 비활성화 하는 방법 (기능을 깍아서 해결)<ul><li><a href="https://torhy.tistory.com/39" target="_blank" rel="noopener noreferrer">https://torhy.tistory.com/39</a></li></ul></li><li><code>CodeIgniter</code> 관련 <code>Stub</code> 추가<ul><li><a href="https://iidaroo.tistory.com/entry/PhpStorm-Codeigniter-%EC%84%A4%EC%A0%95" target="_blank" rel="noopener noreferrer">https://iidaroo.tistory.com/entry/PhpStorm-Codeigniter-설정</a></li><li><a href="https://github.com/bmewburn/vscode-intelephense/issues/1841" target="_blank" rel="noopener noreferrer">https://github.com/bmewburn/vscode-intelephense/issues/1841</a></li></ul></li><li><code>CodeIgniter</code> 관련 확장<ul><li><a href="https://marketplace.visualstudio.com/items?itemName=small.php-ci" target="_blank" rel="noopener noreferrer">https://marketplace.visualstudio.com/items?itemName=small.php-ci</a></li></ul></li><li>관련 <code>Stackoverflow</code><ul><li><a href="https://stackoverflow.com/questions/75020832/intelephense-1014" target="_blank" rel="noopener noreferrer">https://stackoverflow.com/questions/75020832/intelephense-1014</a></li></ul></li></ul></li><li>결론적으로 손쉽게 해결하는 방법을 찾지 못했습니다.</li><li>이것이 왜 어려운 문제인지 살펴보면, <code>Model</code>과 같은 정보가 정적으로 주입되는 것이 아니라 <code>RunTime</code>에 주입되기 때문입니다. 그래서 정적 분석 도구가 이를 찾기 어렵다고 합니다. 이와 관련하여 <code>PHP</code>와 같은 스크립트 언어의 이러한 불편함에 대해 다룬 글들도 있습니다.<ul><li><a href="https://defacto-standard.tistory.com/608" target="_blank" rel="noopener noreferrer">https://defacto-standard.tistory.com/608</a></li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>실 개발 환경은 서버에서 실행되고 있어서 리버스로 찾아서 하느라 힘들었다.</p><p>덕분에 이런 환경도 경험해보게 되었는데 직접 만들면서 Debug의 구성 방식에 대해 많이 배울 수 있었다. 언어 기능이 당연한 것이라고 생각했는데 그렇지 않다는 것도 느끼었고 현대적인 언어가 얼마나 친절하게 제공되고 있는지 감사함을 느낄 수 있었다.</p><p>처음에 이런 환경을 만들기 위해 너무 막막했는데 누군가에게는 도움이 되었으면 좋겠습니다.</p>]]></content>
        <category label="PHP" term="PHP"/>
        <category label="Docker" term="Docker"/>
        <category label="PhpStorm" term="PhpStorm"/>
        <category label="VSCode" term="VSCode"/>
        <category label="Xdebug" term="Xdebug"/>
        <category label="개발 환경" term="개발 환경"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[인프콘 2023 회고]]></title>
        <id>/2023/26/08/infcon-2023-retrospective</id>
        <link href="https://parkgang.github.io/blog/2023/26/08/infcon-2023-retrospective"/>
        <updated>2023-08-26T03:46:00.000Z</updated>
        <summary type="html"><![CDATA[나의 첫 오프라인 컨퍼런스로 인프콘 2023에 다녀왔다!]]></summary>
        <content type="html"><![CDATA[<p>나의 첫 오프라인 컨퍼런스로 <a href="https://www.inflearn.com/conf/infcon-2023" target="_blank" rel="noopener noreferrer">인프콘 2023</a>에 다녀왔다!</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAKAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABgUH/8QAJhAAAgEDAwIHAQAAAAAAAAAAAQIDBAURABIhE0EGBxQWIjJRgf/EABQBAQAAAAAAAAAAAAAAAAAAAAT/xAAaEQADAAMBAAAAAAAAAAAAAAABAhEAAxKR/9oADAMBAAIRAxEAPwAZBVtDVq0kgnSnmwwVdpfbJ9VP7xjI/mtHfzXvW9ularYkefirRTEgdgTjnVHxbQ0nsi6Selg3pCCrdMZUhhyD20ptUET2yjd4kZmhQklQSTtHOk7AtCgSDDaww6ZjafM//9k=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1346" height="1346"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/thumbnail.709ad58.1346.jpg" srcset="/assets/ideal-img/thumbnail.709ad58.1346.jpg 1346w" width="1346" height="1346"></noscript></div></div><p>작년에 <code>인프콘 2022</code> 에 신청했지만 추첨에서 떨어져서 아쉬운 마음으로 다음 인프콘에는 꼭 가겠다는 마음으로 신청을 했는데 다행히 당첨되어서 즐겁게 다녀올 수 있었습니다.</p><p>저에게는 첫 개발 관련 오프라인 컨퍼런스이었기 때문에 어떤 식으로 진행되는지, 어떤 경험인지, 너무 궁금하였고 그래서 설레는 마음으로 행사 일자까지 기다렸습니다.</p><p>이번 컨퍼런스를 다녀오면서 느낀 경험에 대해서 회고해 보려고 합니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="행사장을-가면서">행사장을 가면서<a class="hash-link" href="#행사장을-가면서" title="제목으로 바로 가기">​</a></h2><p>인프콘의 장소는 <code>코엑스</code> 의 <code>그랜드볼룸</code> , <code>아셈볼룸</code> 에서 진행되었습니다.</p><p>저에게 코엑스는 어릴 때 현장 체험 학습 정도로 간 기억만 남아있었고 <code>스타필드 코엑스몰</code> 은 익숙한데 여기서 어디로 가야지 코엑스의 큰 전시장 같은 것이 나오는지 궁금증을 가지고 이동했습니다.</p><p>가보니 <code>스타필드 코엑스몰</code> 을 가로질러 가다 보면 나오더군요. 저에게는 나름대로 건물을 찾고 방문하는 것 자체도 재밌는 경험이었습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="첫인상">첫인상<a class="hash-link" href="#첫인상" title="제목으로 바로 가기">​</a></h2><p>이전 <code>인프콘 2022</code> 사진만을 보고서는 규모와 어떻게 이뤄지는지 체감이 안되었는데 이번에 실제로 방문해 보니 어떤 느낌인지 알겠더군요.</p><p>첫 입구 데스크에서 본인 인증 후 <code>인프콘</code> 웰컴 굿즈 및 백을 받고 입장하게 되면 입구 앞에서 인생 네 컷과 같은 사진 찍을 수 있는 부스와 <code>미니맵</code> 가이드대로 기업 부스와 발표 세션을 들을 수 있는 관들이 쭉 있는 형태이었습니다.</p><p>이게 사람이 글과 사진만으로 얻을 수 있는 것은 한계가 있어서 처음에는 어떤 기업 부스가 있는지, 건물 디자인은 어떤지 넓게 둘러보는데 집중했습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="기업-부스">기업 부스<a class="hash-link" href="#기업-부스" title="제목으로 바로 가기">​</a></h2><p>기업 부스에서 굿즈를 받는 것은 알고 있었는데 가기 전부터 뭘 어떻게 해야 굿즈를 주는 거지? 막 시험 같은 것을 보는 건가? 걱정 반 기대 반으로 <code>인프콘</code> 을 기다리고 있었습니다.</p><p>가보니 기업을 홍보하고 <code>설문 조사</code> 같은 것을 참여하면 굿즈를 주는 형태이더군요.</p><p>대부분 프로세스는 아래와 같았습니다.</p><ol><li>기업 부스 방문</li><li><code>QR</code> 코드를 통해 설문 조사 참여 (대부분 구글 폼으로 되어있음)</li><li>설문 조사 완료 인증 후 굿즈 및 스탬프 받기</li></ol><p>살짝 아쉬웠던 부분은 대부분 구글 폼이었다는 것이었는데 구글 폼으로 참여시 이메일 주소가 <code>Gmail</code> 으로 노출되어서 주 메일이 <code>네이버</code> 인 경우 노출이 안될 수 있다는 점이었습니다.</p><blockquote><p>물론, 양식 중 메일 주소를 따로 입력받는 필드가 있는 경우도 있었습니다.</p></blockquote><p>익히 다른 회고를 통해서 접해셨을지는 모르지만 기업 부스에 워낙 사람이 많아서 정신없이 설문 조사에 참여하고 굿즈를 받으며 점점 무거워지는 제 손을 볼 수 있었습니다.</p><blockquote><p>설문 조사로 무수히 많이 열린 탭들...</p><div class="imgContainer_g3cu spacer_xInT" style="width:150px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAWCAYAAAD5Jg1dAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC9UlEQVQokWWT/U/TVxjFbyaOhPmLGyTKX7A3smJiFwsKU2QJFukS4wtms8tc0GCEKLE/sA3T8IVWDCLapi/7FnwJjRpGzVhTTWyWmUxj1G0hwowGLXZteWmhWiwtxc/CZb/tJjfn3POcnDw39z4iLy+P4uJiVq16CyHE/3ZhYaGsizVr3mH16rdZu/ZdNBoNTU1NmEwmSktLKSoqkub8/HxE4XtFrF+/jo8+/oA6w05aWo5L444dekpKSqS5oKAAYWpWeDwS4c7th4w/jnKs2URj42Hi8Thjo2Mkk0kGBgYQbqdKYiZJeCLG8lLd/dhsNslnZ2clBoNBhMPhJp1dJDX/Sooup4s2syJ5KpWSGAgEEKr7ojxMRhMSHTY3HdbTkudyuf8SbyEs7d2Ex+Pc+32EyMQcbd8pmM0nyeWWiESizL9O4/V6EfuM9QwODeFR+xj+2Y/RaKSqsgbfT4P0eRxc913j6NEjCLXvsoyPxVYuoygddJ9qA+KkkqNAgl+DVxAup8rrdJbxZ89ZyCxisVixWn8gkZjl0aMx5uZSBPzXEE6Xi/RChlDoGZnsIj09PVg6lxMhHp9h6Q34h68gLly6zBsgEpuRxfN2F7Ze07LCwqv7wDi/3VIRxq+/YXBwCNXTh+/6MPX7v2LPrm0Ebzq4OmDhpt+GqeUA4qTSSWxqmqdPnwCLKEo7DQ2NhMNR/vxrhFAojNP1I6LX7iGWyPDP5Dy/eHOc7lI529st23jxYoJsNsONGwHEmfMqky8hFE1x//YSFqudU11d0jg9vfLWPp8f4em/xBIw93Jeip7+izQf/567DyL8MRJm9O8k9nNeRO85O+l0humpqZVP4bDxYWk1Or2Zmr1mPvnsBJU13yKqtm+nXVFobW2ls0Ph89pdvK/Zwu69B6n94kt21x+ibPM2RFlZGZUVFZRv3sKmjRo+rW1gk6GRCt0Gtm6tpK5OT3V1FWJ5TnQ6HeXl5RgMBgx1O9Hra9Dr9Wi1WjZqtXKW/gXP2Wgz3Fdg3gAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1170" height="2532"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.e8f69ac.1170.png" srcset="/assets/ideal-img/1.e8f69ac.1170.png 1170w" width="1170" height="2532"></noscript></div></div></blockquote><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="오프닝">오프닝<a class="hash-link" href="#오프닝" title="제목으로 바로 가기">​</a></h2><p>그렇게 인프콘 행사장에 들어서 사진도 찍고, 기업 부스에서 굿즈도 타고 있다 보니 어느새 오프닝 시간이 다 되더군요.</p><p>오프닝에서는 아래의 내용을 들을 수 있었는데</p><ul><li>인프랩 <code>CEO</code> 님이 인프런이 어떻게 성장하고 있는지, 앞으로의 방향성에 들을 수 있었고</li><li>인프랩 <code>CTO</code> 님이 인프런의 서비스를 어떻게 고도화하고, 앞으로 제품 방향이 무엇인지 들을 수 있었고</li><li>인프랩 <code>커뮤니티 리드</code> 님이 인프콘에 대한 소개와 행사 진행 방식에 대해서 들을 수 있었습니다.</li></ul><p>인상 깊었던 부분은 <code>인프런</code> 이 강좌 플랫폼에 국한되지 않고 <code>랠릿</code> 이라는 서비스로 확장되어 더 큰 그림을 그리고 있다는 것이었습니다.</p><p>서비스가 이렇게 확장될 수 있구나, 비즈니스 모델이 이렇게 만들어질 수 있구나 하는 생각이 들었습니다.</p><p>개인적으로는 저렇게 성장하는 서비스를 만들면 얼마나 재미있을까라는 생각이 들면서 오프닝을 청취하였습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="데브챗"><a href="https://www.inflearn.com/pages/infcon-2023-event-inflearn-devchat" target="_blank" rel="noopener noreferrer">데브챗</a><a class="hash-link" href="#데브챗" title="제목으로 바로 가기">​</a></h2><p><code>오프닝</code> 이 끝나고 이번 행사에서 가장 하고 싶었던 <code>데브챗</code> 을 신청하러 갔습니다.<br>
<!-- -->저는 <code>데브챗</code> 을 위해서 이력서도 새롭게 개편하였거든요.</p><p>진중하게 제 이력서에 대해서 컨펌하는 시간을 가지고 싶었고, 제3의 개발자와 이야기를 나누고 싶었습니다.</p><p>대화를 나눌 수 있는 시간은 <code>20분</code> 으로 체감상 짧았는데 저에게는 정말 좋은 시간이었습니다.</p><p>즐거운 이야기를 나누고 있던 찰나 시간이 부족해서 더 이야기할 수 없는 상황이 되자 먼저 명함을 건네주셔서 감사한 마음으로 명함을 교환하고 마무리하게 되었습니다.</p><p>저의 경우는 <code>이력서</code> 를 통해서 대화가 오고 갔는데 다행히 이력서 평가가 좋아서 나름 자신감도 생기고 내가 열심히 했구나 확인받을 수 있었던 거 같습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="발표-세션">발표 세션<a class="hash-link" href="#발표-세션" title="제목으로 바로 가기">​</a></h2><p>발표의 경우 녹화되어서 올라가기 때문에 모두 듣는 것보다 관심이 가고, 발표 후 <code>QnA</code> 으로 질문이 발생할 수 있는 만한 것을 큐레이션 해서 들었습니다.</p><p>프로젝트를 진행하면서 저와 같은 고민이 발생했던 세션을 중점으로 선택하여 들었습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="raw-query-리팩토링을-통한-주니어-개발자의-성장-이야기---나예진42-서울"><a href="https://www.inflearn.com/conf/infcon-2023/session-detail?id=750" target="_blank" rel="noopener noreferrer">Raw Query 리팩토링을 통한 주니어 개발자의 성장 이야기 - 나예진(42 서울)</a><a class="hash-link" href="#raw-query-리팩토링을-통한-주니어-개발자의-성장-이야기---나예진42-서울" title="제목으로 바로 가기">​</a></h3><p>해당 발표는 제가 프로젝트 진행 당시 DB를 다루면서 동일하게 발생했던 고민인데 이것에 대해서 딱 집어서 발표하는 거 같아 인프콘 오기 전부터 기대하던 세션이었습니다.</p><p>발표를 들으면서 제가 느꼈던 것과 비슷한 고민과 해결 방법을 선택하셔서 발표 이후 <code>QnA</code> 시간이 기대되었습니다.</p><p>발표의 주 내용은 아래와 같았습니다.</p><ul><li>왜 중복되는 Query는 문제인가?</li><li>중복되는 Query을 어떻게 해결할 것이 인가?</li><li>리펙토링 전후 비교</li></ul><p>해결 방법의 경우 중복되는 쿼리를 <code>아톰</code> 형태의 함수로 분리를 하셨더군요.</p><p>저도 프로젝트 진행 당시 그렇게 진행을 하였는데 <code>아톰</code> 형태로 함수를 분리할수록 쿼리의 유연성이 떨어지는 것을 느껴서 이에 대해서 어떻게 해결하고 경험해셨는지 질문하였고 추가적으로 <code>영속성 레이어</code> 에 대해서 테스트, <code>enum</code> 과 같은 유연한 <code>string</code> 의 경우 어떻게 제어하였는지, 리펙토링에 대해서 어떻게 설득했는지 등 물어보았습니다.</p><p><code>QnA</code> 를 통해 제가 선택한 해결 방법과 고민의 방향성이 비슷하다는 것을 느낄 수 있었습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="인프런에서는-수천-개의-테스트-코드를-이렇게-다루고-있어요---이민우인프랩"><a href="https://www.inflearn.com/conf/infcon-2023/session-detail?id=763" target="_blank" rel="noopener noreferrer">인프런에서는 수천 개의 테스트 코드를 이렇게 다루고 있어요 - 이민우(인프랩)</a><a class="hash-link" href="#인프런에서는-수천-개의-테스트-코드를-이렇게-다루고-있어요---이민우인프랩" title="제목으로 바로 가기">​</a></h3><p>이전 <code>Raw Query</code> 세션과 동일하게 프로젝트에서 고민이 많았던 부분에 대한 발표라서 듣게 되었습니다.</p><p>주로, <code>Unit Test</code> 를 작성하면서 <code>DB</code> 레이어 관련 부분을 어떻게 테스트하는가? 가 궁금했던 것인데 발표를 들으면서 아래의 내용을 인상 깊게 들을 수 있었습니다.</p><ul><li><code>DTO</code> 를 통해서 <code>Input</code> 값에 대해서 테스트하셨구나</li><li>대부분 정상 시나리오에서 통과하는 테스트를 <code>Happy Path</code> 라고 말하는구나</li><li><code>DB</code> 클린업을 하지 않고 테스트의 시작과 종료를 <code>트랜잭션</code> 으로 묶어서 롤백 하는 방법으로 퍼포먼스 개선을 할 수도 있구나</li></ul><p>발표가 끝나고 <code>QnA</code> 시간에 제가 스스로 <code>Unit Test</code> 를 도입하면서 겪었던 고민을 질문할 수 있었고 답변을 통해 제가 고민하던 여러 선택지 중 어떤 것이 맞는 방향인지 통찰력을 얻을 수 있었습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="기적의-4일-go-성능-선착순-예매-시스템-구현---김재훈인프랩"><a href="https://www.inflearn.com/conf/infcon-2023/session-detail?id=776" target="_blank" rel="noopener noreferrer">기적의 4일! Go-성능 선착순 예매 시스템 구현 - 김재훈(인프랩)</a><a class="hash-link" href="#기적의-4일-go-성능-선착순-예매-시스템-구현---김재훈인프랩" title="제목으로 바로 가기">​</a></h3><p>마지막 발표로 듣게 되었는데 제가 <code>Golang</code> 에 관심이 많은데 <code>인프런</code> 에서도 <code>Golang</code> 을 쓴다고?라는 생각으로 듣게 되었습니다.</p><p>발표에서 인상 깊게 들은 내용은 아래와 같습니다.</p><ul><li>사용자가 많이 몰릴 때 기다리라고 안내하는 것을 <code>가상 대기열</code> 이라고 하는구나</li><li>인프런의 <code>DevOps</code> 팀에서는 <code>Golang</code> 도 사용하는구나</li><li><code>Node.js</code> 성능 개선을 위해서 <code>스케일 업</code> 이 아닌 <code>스케일 아웃</code> 으로 해결했구나</li><li>성능 개선을 위해서 <code>커넥션</code> 관리를 중점으로 내용을 들을 수 있었다.</li><li><code>스케일 아웃</code> 을 하다 보면 <code>커넥션</code> 이 많아져 이를 관리하기 위해서 <code>Proxy</code> 가 필요할 수 있다는 사실을 알 수 있었다.</li><li><a href="/blog/2021/12/26/belf-project-retrospective/">belf</a> 프로젝트에서 <a href="https://github.com/belf-kr/oauth-server" target="_blank" rel="noopener noreferrer"><code>Golang</code> 을 사용하는 제품</a> 에서 <code>Docker</code> 의 <code>멀티 스테이지 빌드</code> 를 통해 <code>Docker</code> 의 <code>Image</code> 를 가볍게 하는 것을 경험할 수 있었는데 해당 발표에서도 <code>멀티 스테이지 빌드</code> 으로 얻는 이점을 소개해 줘서 반가웠다.</li></ul><p>실제로 경험하지 않은 것에 대한 것들이라 이런 것들이 있구나 하고 알 수 있었습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="기타">기타<a class="hash-link" href="#기타" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="스탬프-투어"><a href="https://www.inflearn.com/pages/infcon-2023-event-inflearn-stamp" target="_blank" rel="noopener noreferrer">스탬프 투어</a><a class="hash-link" href="#스탬프-투어" title="제목으로 바로 가기">​</a></h3><p><code>기업 부스</code> 및 <code>이벤트</code> 를 통해서 <code>스탬프</code> 를 모아서 추첨을 통해 <code>인프콘</code> 굿즈를 받을 수 있었습니다.</p><p><code>인프콘</code> 에서 가장 줄이 긴 부분이 해당 부스이더군요.</p><blockquote><p>역시 다들 <code>인프콘</code> 의 굿즈가 탐나는 걸까요?</p></blockquote><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="콘텐츠-존"><a href="https://www.inflearn.com/pages/infcon-2023-event-inflearn-contents" target="_blank" rel="noopener noreferrer">콘텐츠 존</a><a class="hash-link" href="#콘텐츠-존" title="제목으로 바로 가기">​</a></h3><p>주사위를 굴리는 콘텐츠가 있었는데 가볍게 <code>스탬프</code> 를 찍기 좋았습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="점심">점심<a class="hash-link" href="#점심" title="제목으로 바로 가기">​</a></h3><p>어디서 먹을지 고민되는 부분이었는데 보니까 행사장 출입구에서 손쉽게 <code>스타필드</code> 로 내려갈 수 있는 <code>에스컬레이터</code> 가 있더군요.</p><p><code>스타필드</code> 에서 손쉽게 점심을 해결할 수 있었습니다.</p><p>다만, 점심도 그렇고 커피를 구매하려면 사람이 많아서 많이 기다려야 하더군요.<br>
<!-- -->그래서 원하는 발표가 있다면 시간 계산을 잘해야 될 거 같습니다.</p><p>그리고 발표장 들어갈 때 커피는 반입이 안되므로 빨리 먹고 가는 것도 팁입니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="네트워킹-파티"><a href="https://www.inflearn.com/pages/infcon-2023-network" target="_blank" rel="noopener noreferrer">네트워킹 파티</a><a class="hash-link" href="#네트워킹-파티" title="제목으로 바로 가기">​</a></h2><p>오프라인 행사이므로 <code>네트워킹</code> 을 활발히 해보고 싶어서 해당 프로그램에 대해서도 기대가 많았는데 어떻게 진행될지 예상이 안되는 프로그램이기도 했습니다.</p><p>행사장에 들어가 보니 많은 사람들이 있었고 입구에서 과자 등이 들어가 있는 간식 꾸러미를 나눠주더군요.</p><p>스탠딩 파티 형식이었으며 자유롭게 가서 말을 걸면 되는 구조이었습니다.</p><p>가면 야광팔지를 낄 수 있는데 이게 혼자서는 손목에 끼기가 어려워서 옆 분께 도와달라고 하면서 대화의 운을 때게 되었습니다.</p><p>저와 다른 직군인 <code>ML</code> 개발자 분과 만나서 이야기도 할 수 있었고 돌아다니면서 <code>인프런</code> 직원분들과도 개발에 대해서 여러 이야기를 나눌 수 있었습니다.</p><p><code>네트워킹 파티</code> 에는 참여한 각 기업의 <code>HR 담당자</code> , <code>현업 개발자</code> 분들도 있어서 <code>실무</code> 와 관련된 이야기도 할 수 있어서 재미있었습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="클로징">클로징<a class="hash-link" href="#클로징" title="제목으로 바로 가기">​</a></h2><p>열심히 행사를 즐기고 나니 벌써 종료될 시간이 다 되었더군요...</p><p>클로징 부분에서는 <code>인프콘</code> 에 대해서 <code>숫자</code> 로 설명하는 프레젠테이션을 볼 수 있었습니다.</p><ul><li>인프콘의 예산은?</li><li>인프콘에 참여한 사람은?</li><li>인프콘 발표 중 가장 많이 들어온 세션의 인원은?</li><li>etc.</li></ul><p>총 <code>8,712</code> 명 정도 행사에 지원했으며 그중 <code>1,800</code> 명 정도 뽑았다고 하니 <code>4.84</code> 정도의 경쟁률이었더군요.</p><p>전체적으로 <code>수련회</code> 온 거 같은 분위기이었고 다 같이 사진을 찍으면서 마무리할 수 있었습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="후기">후기<a class="hash-link" href="#후기" title="제목으로 바로 가기">​</a></h2><p>리스트로 나열하자면 아래와 같습니다.</p><ul><li>노트북이 필요할 거 같아서 가져왔는데 괜히 가져왔다. 딱히 쓸 일이 없네</li><li><code>QnA</code> 시간이 너무 좋았다. 궁금한 것에 대해서 바로 답변 받을 수 있는 부분이 매력적이었다.</li><li>설문조사에서 직무가 <code>Front-end</code> , <code>Back-end</code> , etc. 으로 나눠있더라 포함되는 직무가 없을 때 뭔가 모를 섭섭함이 있었다.<blockquote><p><code>Full-stack</code> ...</p></blockquote></li><li>혼자 오더라도 문제는 없겠지만 확실히 누군가와 함께 오는 것이 훨씬 재미있어 보인다. 개발 관련 이야기를 누군가와 함께 할 수 있는 그 행복이 제일 크다고 느꼈다.</li><li>사람들과 대화를 많이 하다 보니 자연스럽게 <code>포지션</code> 에 대해서 소개하는 부분이 많았는데 <code>포지션</code> 에 대한 고민이 많아지게 되는 계기가 되었던 거 같다.</li><li>각종 기업에 대한 정보가 부족해서 기업 부스와 발표자분의 소속을 이해하지 못하고 들은 부분이 아쉬웠다. 어떤 소속인지, 어떤 것을 하는 곳인지 알고 가면 더욱 재밌게 즐길 수 있을 거 같다.</li><li>한 곳에 <code>IT</code> 직종의 사람들이 모이기 때문에 어떤 분위기를 가진 분들이 오실지 관심이 있었다. 보니까 다들 이쁘고 멋지신 분들이 많더라 동종업계 분들의 멋지신 모습을 보니 뭔가 모를 뿌듯함이 있었다.</li><li>출발 전 받은 굿즈를 어떻게 보관하지? 어떤 가방을 가져갈까 고민했는데 입장하면서 백을 하나 주더라 결론적으로 가방 고민도 필요가 없는 부분이었다.</li><li>마지막에 <code>향로</code> 님 만나 뵙고 사진을 찍으면서 마무리할 수 있었다. 🥳</li></ul><p>막연하게 몰랐던 오프라인 컨퍼런스를 다녀오며 여러 경험을 할 수 있던 거 같습니다.</p><p>컨퍼런스에서 개발 관련 지식을 배웠다 보다는 다양한 사람을 만나고, 행사장의 분위기는 어떻고, 어떤 주제에 대해 다들 관심이 많구나, etc. 를 경험할 수 있었던 거 같습니다.</p><p>저는 <code>데브쳇</code> 과 발표 후 <code>QnA</code> 시간이 가장 유익했습니다.<br>
<!-- -->같은 주제에 대해서 대화할 수 있다는 것이 매력적이더군요.</p><p>다음에 <code>인프콘</code> 에 또 참여하게 될지는 모르겠지만 더 성장한 모습으로 다시 참여하고 싶더군요.</p><p>여러 좋은 경험을 하게 이러한 행사를 만들어주신 <code>인프랩(인프런)</code> 직원분들께 감사드리며 회고를 읽어주셔서 감사합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAIAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAQF/8QAIBAAAQMEAgMAAAAAAAAAAAAAAQIDBQAEBhESFBVhkf/EABUBAQEAAAAAAAAAAAAAAAAAAAEE/8QAGxEAAgMAAwAAAAAAAAAAAAAAAQIAAxIhgZH/2gAMAwEAAhEDEQA/AJsyhJ+cyGJterdXEba7SXGnE8NKPEqKVnewAK1xi0WAB4e6T6Druh8VSlSvVlVyxHcXvIJ4Hk//2Q==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1024" height="769"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.62ff23a.1024.jpeg" srcset="/assets/ideal-img/2.62ff23a.1024.jpeg 1024w" width="1024" height="769"></noscript></div></div>]]></content>
        <category label="인프콘" term="인프콘"/>
        <category label="컨퍼런스" term="컨퍼런스"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Next.js의 prefetch는 어떻게 동작하는가?]]></title>
        <id>/2023/07/15/how-nextjs-prefetch-work</id>
        <link href="https://parkgang.github.io/blog/2023/07/15/how-nextjs-prefetch-work"/>
        <updated>2023-07-15T11:00:00.000Z</updated>
        <summary type="html"><![CDATA[Next.js 에서 prefetch 를 사용하면 미리 데이터가 로드되어 페이지 로드 시간이 단축된다는데 꼭 Next.js 의 Link 컴포넌트가 아니면 달성이 불가능한 것인가? SSR 인 경우는 어떻게 되는가? 미리 불러오면 안 될 텐데? 와 같은 호기심이 발생했습니다. 어떻게 동작하는지 확인해 봅시다!]]></summary>
        <content type="html"><![CDATA[<p><code>Next.js</code> 에서 <code>prefetch</code> 를 사용하면 미리 데이터가 로드되어 페이지 로드 시간이 단축된다는데 꼭 <code>Next.js</code> 의 <code>Link</code> 컴포넌트가 아니면 달성이 불가능한 것인가? <code>SSR</code> 인 경우는 어떻게 되는가? 미리 불러오면 안 될 텐데? 와 같은 호기심이 발생했습니다. 어떻게 동작하는지 확인해 봅시다!</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="개요">개요<a class="hash-link" href="#개요" title="제목으로 바로 가기">​</a></h2><p><code>Next.js</code> 에서는 큰 문제가 없는 이상 <code>&lt;a&gt;</code> 보다는 <code>&lt;Link&gt;</code> Component를 사용하는 것이 더 나아 보입니다.</p><p>그런데 <code>prefetch</code> 에 대해서 아래의 의문이 생겼습니다.</p><ul><li><code>Next.js</code> 의 <code>Link</code> 컴포넌트가 아니면 달성이 불가능한 것인가?</li><li><code>SSR</code> 인 경우는 어떻게 되는가? 미리 불러오면 안 될 텐데</li></ul><p>궁금증을 해결하기 위해서 프로젝트를 만들어서 실험해 보았습니다.</p><p>실험한 프로젝트 코드는 <a href="https://github.com/parkgang/next.js-prefetch" target="_blank" rel="noopener noreferrer">parkgang/next.js-prefetch</a> 에서 확인할 수 있습니다.</p><p>코드를 보고 실제로 어떻게 동작하는지, 여러 컨셉을 시도해볼 수 있습니다!</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="insight">Insight<a class="hash-link" href="#insight" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="prod-에서만-활성화-된다"><code>Prod</code> 에서만 활성화 된다.<a class="hash-link" href="#prod-에서만-활성화-된다" title="제목으로 바로 가기">​</a></h3><p>어쩐지 열심히 테스트해도 안되길래 뭐지 했는데 <code>Prod</code> 에서만 활성화된다고 합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAlklEQVQImU1OywoCMQzs//+QRxHRf/Dqi1a22zTdvix0JDksBiYvZpIxtVbknNF7R2sNYwzMOXdICMds2wZrLZxz8N4jxogQAogIzKw7OWSELct1XbUKaVkWJUgtpegnIymlpOpcCiKzXhVQpN2SkYZCAAfCtzX19B/iufUOI8ODPA7PG47+jmtyONMbF7Y40QufUVXwAyhh5jaYTyKNAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="810" height="512"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.c7cfdf3.810.png" srcset="/assets/ideal-img/1.c7cfdf3.810.png 810w" width="810" height="512"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="link-컴포넌트-이외-routerprefetch-으로도-미리-로드할-수-있다"><code>Link</code> 컴포넌트 이외 <code>router.prefetch</code> 으로도 미리 로드할 수 있다<a class="hash-link" href="#link-컴포넌트-이외-routerprefetch-으로도-미리-로드할-수-있다" title="제목으로 바로 가기">​</a></h3><p>공식 문서 <a href="https://nextjs.org/docs/pages/api-reference/functions/use-router#routerprefetch" target="_blank" rel="noopener noreferrer">router.prefetch</a> 에 작성되어 있습니다.</p><p>코드의 컨셉을 보면</p><ul><li><code>로그인</code> 하는 페이지에 들어왔을 때 미리 <code>Dashboard</code> 경로의 정보를 <code>prefetch</code> 하고</li><li><code>로그인</code> 이 완료되면 빠르게 <code>Dashboard</code> 으로 넘어갈 수 있도록 하는 것입니다.</li></ul><div class="language-tsx codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-tsx codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> useCallback</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> useEffect </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"react"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> useRouter </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"next/router"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Login</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> router </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useRouter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> handleSubmit </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useCallback</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    e</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">preventDefault</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">fetch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/api/login"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      method</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"POST"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      headers</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token string-property property" style="color:#36acaa">"Content-Type"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      body</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token known-class-name class-name">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">stringify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">/* Form data */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">then</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">res</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Do a fast client-side transition to the already prefetched dashboard page</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">res</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">ok</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> router</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">push</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/dashboard"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">useEffect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Prefetch the dashboard page</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    router</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">prefetch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/dashboard"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">router</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">form</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onSubmit</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">handleSubmit</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token comment" style="color:#999988;font-style:italic">/* Form fields */</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">type</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">submit</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">Login</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">form</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="prefetch-라고-해서-ssr-이-미리-되는-것은-아니다-prefetch-는-정적-자료-만-미리-불러오는-것이다"><code>prefetch</code> 라고 해서 <code>SSR</code> 이 미리 되는 것은 아니다. <code>prefetch</code> 는 <code>정적 자료</code> 만 미리 불러오는 것이다.<a class="hash-link" href="#prefetch-라고-해서-ssr-이-미리-되는-것은-아니다-prefetch-는-정적-자료-만-미리-불러오는-것이다" title="제목으로 바로 가기">​</a></h3><p><code>prefetch</code> 는 <code>정적 자료</code> 만 미리 가져오며 <code>prefetch</code> 라고 해서 <code>getServerSideProps</code> 함수를 미리 수행하지 않는다고 합니다.</p><p>실험 결과 <code>prefetch</code> 시 <code>PageComponent</code> 에서 미리 사용할 수 있는 형태로 <code>JS</code> 을 가공해 놓는 것을 볼 수 있었습니다.</p><div class="language-tsx codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-tsx codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">SlowImages</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@/components/SlowImages"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token maybe-class-name">GetServerSideProps</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"next"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">PageProps</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ssrFinalizedDisplayDate</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> getServerSideProps</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">GetServerSideProps</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">PageProps</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// prefetch과 관련 없이 SSR이 잘 이뤄지는지 보기위해서 의도적으로 기다립니다.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name builtin">Promise</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">resolve</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">setTimeout</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">resolve</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    props</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      ssrFinalizedDisplayDate</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Date</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">toLocaleString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"ko-KR"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">SsrPage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ssrFinalizedDisplayDate</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> nowDateIso</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">PageProps</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">SSR Page!</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">p</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        SSR이 완료된 시점은 </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">strong</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">nowDateIso</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">strong</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"> 입니다.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">p</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">SlowImages</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAhklEQVQImUWM2wrCMBQE8/9fJAj9BaWvPlq8pdWS5trkZKSR4sC+7CyrjqcnXa/p+pHDeeZyswTnuF9HtJ5YnCfEhBqXzNRSeBnBRmHDzAb90OQQqCIoqE3siPyGuRSmYSB93pRcUCKVWv8pIkSXWEzE24RkaVdqkzv70FmPt5F1rdQirf8Cd7zBnP39ywoAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1458" height="748"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.6f383bc.1458.png" srcset="/assets/ideal-img/2.6f383bc.1458.png 1458w" width="1458" height="748"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="image-까지는-미리-불러오지-않는다"><code>Image</code> 까지는 미리 불러오지 않는다.<a class="hash-link" href="#image-까지는-미리-불러오지-않는다" title="제목으로 바로 가기">​</a></h3><p>실험 시 <code>image</code> 도 미리 불러오는지 궁금해서 확인해 보았는데 그렇지 않았습니다.</p><p><code>Next.js</code> 측에서 <code>Image</code> 의 경우 <code>Image</code> 컴포넌트로 최적화하는 방법으로 사용하라는 것이면서도 사실 <code>Image</code> 는 미리 불러와서 이득이 없다고 판단한 거 같습니다.</p><p>페이지에 이미지가 몇 개일 지 모르는데 그걸 어떤 기준으로 모두 확보하느냐, 그래서 화면에 표시되는 이미지만 미리 처리한다 이런 컨셉 같습니다.</p><p>실험한 프로젝트에서 보면 <code>Next.js</code> 의 <code>Image</code> 컴포넌트를 사용하면 <code>Image</code> 를 불러오는 이미지 URL이 <code>http://localhost:3000/_next/image?url=</code> 형식으로 바뀌면서 알아서 캐시 되더군요.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="prefetch-시-미리-불러오는-데이터-형식은"><code>prefetch</code> 시 미리 불러오는 데이터 형식은?<a class="hash-link" href="#prefetch-시-미리-불러오는-데이터-형식은" title="제목으로 바로 가기">​</a></h3><p>아래와 같습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAZklEQVQImSXLuxLCIBRAQf7/7zQTDVopzws4gDED3Unh9quM88SUiZKQlJFc6N+d2jshCtYHfBBU8Ja7fnBZVq7Lym3T5FwYYyAivN6GTT9Re7H/6TzGOpwPtNaZc1JrI5cPv+PgBKNfb0ezx6k1AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="674" height="228"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.602494c.674.png" srcset="/assets/ideal-img/3.602494c.674.png 674w" width="674" height="228"></noscript></div></div><p><code>SSG</code> 는 아래와 같은 느낌으로 불러오더군요.</p><div class="language-tsx codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-tsx codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">SlowImages</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@/components/SlowImages"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">SsgPage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">SSG Page!</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">p</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">ssg 페이지 입니다.</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">p</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">SlowImages</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAaklEQVQImR2JUQrCMBAFc/8j6Ue9gVD0S0SpxZpWLI1ZyTYbR+KDgRmeO19vnC4d/TAyB2Fe3kQRpscLP0x/T7rimvbJZj+yOy5sDx/aLlFn2fB3j0apgbMCpR4FNH/RXDCrGCFEVISiiR8OCXInz+KfPwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1364" height="430"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.f2521c5.1364.png" srcset="/assets/ideal-img/4.f2521c5.1364.png 1364w" width="1364" height="430"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="결론">결론<a class="hash-link" href="#결론" title="제목으로 바로 가기">​</a></h2><ul><li><code>prefetch</code> 으로 미리 불러오는 데이터는 <code>CSS</code> , <code>JS</code> 이다.<ul><li><code>image</code> 의 경우 미리 불러오지 않는다.</li><li><code>CSS</code> 도 미리 불러오는지까지는 확인하지 못함</li></ul></li><li><code>prefetch</code> 되었다고 해서 <code>SSR</code> 안 되는 것이 아니다.<ul><li><code>getServerSideProps</code> 은 <code>prefetch</code> 단계에서 수행되지 않는다.</li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>이렇게 <code>Next.js</code> 의 <code>prefetch</code> 에서 헷갈릴 만한 것들을 알아보았습니다.</p><p>단순하게 미리 불러온다는 라는 개념보다 어떤 경우, 어떻게 불러오는지 알 수 있어 더 섬세하게 사용할 수 있을 거 같습니다.</p><p>읽어주셔서 감사합니다.</p>]]></content>
        <category label="nextjs" term="nextjs"/>
        <category label="prefetch" term="prefetch"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[오픈 소스는 어떻게 Git Commit 컨벤션을 지켜서 CHANGELOG를 관리하는가]]></title>
        <id>/2023/06/20/git-commit-convention-changelog</id>
        <link href="https://parkgang.github.io/blog/2023/06/20/git-commit-convention-changelog"/>
        <updated>2023-06-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[내가 알고 있던 Git Commit 컨벤션 다른 사람들은 어떻게 사용할까? 오픈 소스 사례를 봐보자]]></summary>
        <content type="html"><![CDATA[<p>내가 알고 있던 Git Commit 컨벤션 다른 사람들은 어떻게 사용할까? 오픈 소스 사례를 봐보자</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="개요">개요<a class="hash-link" href="#개요" title="제목으로 바로 가기">​</a></h2><p><code>Git</code> 을 사용하면서 한 번쯤 Commit 메시지를 어떻게 작성해야 할지 고민했던 적이 있을 것이다.</p><p>왜 Commit 메시지를 고민할까? 여러 이유가 있겠지만 수정한 의도와 유지 보수를 위함일 것이다. (조금 더 생산적인 마인드로 생각해 보면 메시지를 잘 적으면 팀원이 나에게 어떤 의도를 가지고 수정했는지 물어보지 않을 수 있다!)</p><p>관련해서 찾아보면 다들 이러한 고민을 하고 있었고, 많이 발생하는 Case에 대해서 잘 정리된 내용을 볼 수 있을 것이다.</p><p>실제로 적용해서 사용하다 보면 내가 정말 잘 사용하고 있는 것이 맞을까? 다른 사람은 어떻게 사용하는지 궁금증이 발생하기 마련인데 남의 회사 코드를 볼 수 없으니… 오픈소스 프로젝트를 레퍼런스 해보자.</p><p>또한, Commit 메시지를 잘 작성하면 손쉽게 패치 노트를 작성할 수 있다. 오픈 소스의 Releases를 보면 아래와 같은 형식으로 Git Commit이 쭉 나열되거나 <code>CHANGELOG.md</code> 작성된 것을 볼 수 있는데 어떻게 나열하는지도 봐보자</p><div class="codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-text codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">🚨 fix: ??? 수정 #1801</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">✨ feat(clipboard): ??? 개발 #1807</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">⬆️ chore(deps): ??? 버전 업 #1815</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">📝 docs: ??? 문서 보강 #2055</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="내가-참고하던-commit-컨벤션">내가 참고하던 Commit 컨벤션<a class="hash-link" href="#내가-참고하던-commit-컨벤션" title="제목으로 바로 가기">​</a></h2><p>라이브러리를 사용하면서 봤던 <code>README.md</code> 중 <code>React Query</code> 가 깔끔하게 Commit 컨벤션이 정리되어 있어서 자주 참고했다.</p><p>좋아하던 라이브러리이기도 했고 활발한 오픈소스에서 사용되기에 신뢰가 있었다.</p><p><a href="https://github.com/TanStack/query/blob/main/CONTRIBUTING.md#type" target="_blank" rel="noopener noreferrer">https://github.com/TanStack/query/blob/main/CONTRIBUTING.md#type</a> 에 아래와 같이 작성되어 있다.</p><table><thead><tr><th>type</th><th>설명</th></tr></thead><tbody><tr><td>feat</td><td>새로운 기능</td></tr><tr><td>fix</td><td>버그 수정</td></tr><tr><td>docs</td><td>문서만 변경</td></tr><tr><td>style</td><td>코드의 의미에 영향을 주지 않는 변경 사항(공백, 서식 지정, 세미콜론 누락 등)</td></tr><tr><td>refactor</td><td>버그를 수정하거나 기능을 추가하지 않는 코드 변경</td></tr><tr><td>perf</td><td>성능을 향상시키는 코드 변경</td></tr><tr><td>test</td><td>누락된 테스트 추가 또는 기존 테스트 수정</td></tr><tr><td>chore</td><td>문서 생성과 같은 빌드 프로세스 또는 보조 도구 및 라이브러리 변경</td></tr></tbody></table><p>그런데 이건 일부의 Case이고 더 자세히 정리된 내용이 없을까 찾아보았다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="conventional-commits">Conventional Commits<a class="hash-link" href="#conventional-commits" title="제목으로 바로 가기">​</a></h2><p><a href="https://www.conventionalcommits.org/ko/" target="_blank" rel="noopener noreferrer">Conventional Commits</a> 를 찾을 수 있었다.</p><p>내용을 읽어보니 <a href="https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines" target="_blank" rel="noopener noreferrer">앵귤러 컨벤션</a> 을 기반의 내용도 등장하고, <code>React Query</code> 에서도 <a href="https://github.com/TanStack/query/blob/main/CONTRIBUTING.md#commit-message-conventions" target="_blank" rel="noopener noreferrer">https://github.com/TanStack/query/blob/main/CONTRIBUTING.md#commit-message-conventions</a> 에 따라 <a href="https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#-git-commit-guidelines" target="_blank" rel="noopener noreferrer">Angular Commit Message Conventions</a> 을 따라 한다고 하니 잘 정리된 문서로 확인되었다.</p><p>내가 기존에 알고 있던 것과 더불어 <code>Conventional Commits</code> 를 통해 알게 된 것은 아래와 같다.</p><ul><li><p><code>type</code> 이외 <code>&lt;타입&gt;[적용 범위(선택 사항)]: &lt;설명&gt;</code> 라는 <code>적용 범위</code> 에 대해서 명시하는 것</p><ul><li><code>feat</code> 를 하면서도 이게 어떤 것을 의미하는지 구분할 수 있나? 생각이 들었는데 <code>적용 범위</code> 라는 컨벤션이 있다는 것을 알게 됨</li></ul></li><li><p>Git Commit 컨벤션을 지키는 이유 중 하나로 <code>CHANGELOG.md</code> 작성을 자동화하기 위함이라는 것</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAd0lEQVQImSWKQQ6DIADA+P8XFwc6FdAxQQUJMdTomvTSVNSvAzOBtmBnGA30+t+AelsrIv8cc/PCyTdLK3GyIXpPLoWc8zPdiHXbUKqlHwakUnTdB2Mso9bYaXrm8zwRpRRCCHgfWBZPCCsxRmJKHMfBtu+klLgA2eeYn2gtOtoAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1260" height="416"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.b394886.1260.png" srcset="/assets/ideal-img/1.b394886.1260.png 1260w" width="1260" height="416"></noscript></div></div><blockquote><p><a href="https://www.conventionalcommits.org/ko/v1.0.0/#%EC%99%9C-conventional-commits%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C%EC%9A%94" target="_blank" rel="noopener noreferrer">https://www.conventionalcommits.org/ko/v1.0.0/#왜-conventional-commits를-사용할까요</a></p></blockquote></li><li><p>Breaking Change의 경우 footer 혹은 type에 <code>!</code> 을 작성하더라. 그리고 <code>BREAKING CHANGE:</code> 으로 내용에 대해서 서술하는 것. 이를 통해서 <code>메이저 버전</code> 이 꼭 개선 및 기능 추가만을 의미하는 것이 아니라는 것을 알게 됨</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAK0lEQVQImR3GsREAIAwCQPdfUlOEBtAR8C5f/Wog+1S6Edl5cq49JxVK8w/48yVf/Vqb7AAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1846" height="154"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.6b5683c.1846.png" srcset="/assets/ideal-img/2.6b5683c.1846.png 1846w" width="1846" height="154"></noscript></div></div><blockquote><p><a href="https://www.conventionalcommits.org/ko/v1.0.0/#%EA%B0%9C%EC%9A%94" target="_blank" rel="noopener noreferrer">https://www.conventionalcommits.org/ko/v1.0.0/#개요</a></p></blockquote><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAQUlEQVQImTWIQQrAIAzA/P9HxyZdq7YOppChsENISHo9eG6j609hvVGd0YKxek5Sjc6RhVOUrIVLDG2Bed/eXZ0PUmhMy+P0VIoAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1158" height="288"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.f404818.1158.png" srcset="/assets/ideal-img/3.f404818.1158.png 1158w" width="1158" height="288"></noscript></div></div><blockquote><p><a href="https://www.conventionalcommits.org/ko/v1.0.0/#breaking-change-%EA%BC%AC%EB%A6%AC%EB%A7%90%EA%B3%BC-%EB%A5%BC-%ED%95%A8%EA%BB%98-%ED%8F%AC%ED%95%A8%ED%95%9C-%EC%BB%A4%EB%B0%8B-%EB%A9%94%EC%84%B8%EC%A7%80" target="_blank" rel="noopener noreferrer">https://www.conventionalcommits.org/ko/v1.0.0/#breaking-change-꼬리말과-를-함께-포함한-커밋-메세지</a></p></blockquote></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="실제-react-query는-어떻게-사용하는가">실제 React Query는 어떻게 사용하는가?<a class="hash-link" href="#실제-react-query는-어떻게-사용하는가" title="제목으로 바로 가기">​</a></h2><p>이제 실제 어떻게 사용되는지 궁금해졌다. 한번 <code>React Query</code> 의 <code>PR</code> 을 살펴보자.</p><ul><li><p>커밋 컨벤션을 보면 커밋 타입에 맞게 시멘틱 버저닝이 어디에 배치되는지 결정되던데 이러면 커밋 타입을 누군가 정확하게 분류해야 된다는 것을 시사합니다. 그렇게 어떻게 이걸 잘 지킬 수 있지? 했는데 기여 가이드라인을 보니까 해당 힌트를 얻을 수 있었습니다. 보면 <code>메인테이너</code> 가 <code>스쿼시</code> 로 하면서 알맞은 <code>커밋 타입</code> 으로 바꾸는 것으로 관리하는 것을 알 수 있었습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAABYlAAAWJQFJUiTwAAAALUlEQVQImR3FSQoAIAwDQP//0EIVupj0HMG5zNruOmbqCGWWqloANTMiqQv8HwfJJh9f2Y0PAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1774" height="272"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.2d529eb.1774.png" srcset="/assets/ideal-img/4.2d529eb.1774.png 1774w" width="1774" height="272"></noscript></div></div><blockquote><p><a href="https://github.com/TanStack/query/blob/ae015f1cd6b3157c333fac66cf8078aabe0f4998/CONTRIBUTING.md#pull-requests" target="_blank" rel="noopener noreferrer">https://github.com/TanStack/query/blob/ae015f1cd6b3157c333fac66cf8078aabe0f4998/CONTRIBUTING.md#pull-requests</a></p></blockquote></li><li><p><code>PR</code> 에 대해서는 <code>commit</code> 을 막? 해도 되고 최종적으로 <code>스쿼시</code> 될 때 <code>PR</code> 의 제목으로 하나의 커밋이 들어가기 때문에 <code>PR</code> 제목이 중요하다는 것을 알 수 있었습니다. (물론 <code>GitHub</code> 에서 <code>merge</code> 전 <code>PR</code> 제목과 다르게 수정이 가능하긴 하지만요) 이를 통해서 잘게 <code>PR</code> 이 올라와야 한다는 것을 느낄 수 있었다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAX0lEQVQImR2KQQ6DMAzA+P/v9gMOhaoTMFALa2gSI3qwfLCH8TOxhMAaZ2J4iaQ5Uc4Ld+dfhSrCkH+ZY99pIqgqzcEAc++jmnO/47FtpO9CKQWT2qObdTdVrnojTXkAlih0CmQt3+kAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2502" height="810"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.99ba22f.2502.png" srcset="/assets/ideal-img/5.99ba22f.2502.png 2502w" width="2502" height="810"></noscript></div></div><blockquote><p><a href="https://github.com/TanStack/query/pull/5242/commits" target="_blank" rel="noopener noreferrer">https://github.com/TanStack/query/pull/5242/commits</a></p></blockquote></li><li><p>또 다른 예제로 <code>PR</code> 제목과 실제 <code>merge</code> 된 이름이 다른 것을 보면 <code>메인테이너</code> 가 알아서 바꾼다는 것을 알 수 있었다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAZElEQVQImVWKQQqDMBQFvf/JijupVhepdSM1oj/ZmJ+G4BTFjQ9mmMUrUkqoKr8Y0RCIqty274cojl7eBlM+mF41Y13xbZ7YtsF2LbMZiN5dx2Wl7z84EWZrERG88ydbUHLO/AGKFXHdrezW1gAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="850" height="236"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.3b2c90c.850.png" srcset="/assets/ideal-img/6.3b2c90c.850.png 850w" width="850" height="236"></noscript></div></div><blockquote><p><a href="https://github.com/TanStack/query/commit/7cd2d192e6da3df0b08e334ea1cf04cd70478827" target="_blank" rel="noopener noreferrer">https://github.com/TanStack/query/commit/7cd2d192e6da3df0b08e334ea1cf04cd70478827</a></p></blockquote><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAXElEQVQImUXKWw4CIRAFUfa/QBdglBicD4IyQ3dfKB+JsZL6OymfLpTzlS1ntlvh0QcWk7XW9+MwzJ3U206rFTdDIUYI6Q9DkwiRnq1Syp3eO9OMTz8kiX04/oYv0/10sCPkI58AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2544" height="822"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.00dd0c8.2544.png" srcset="/assets/ideal-img/7.00dd0c8.2544.png 2544w" width="2544" height="822"></noscript></div></div><blockquote><p><a href="https://github.com/TanStack/query/pull/5231/commits" target="_blank" rel="noopener noreferrer">https://github.com/TanStack/query/pull/5231/commits</a></p></blockquote></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="changelog-작성법">CHANGELOG 작성법<a class="hash-link" href="#changelog-작성법" title="제목으로 바로 가기">​</a></h2><p>Git Commit 컨벤션을 지키는 이유 중 하나로 <code>CHANGELOG.md</code> 작성을 자동화하기 위함이라는 내용이 있었다.</p><p><code>CHANGELOG</code> 가 뭘까? <code>changelog 작성법</code> 검색을 통해서 알아낼 수 있었습니다. 핵심은 git commit 컨벤션에 맞게 잘 commit 하면 알아서 type에 맞게 분류해 준다는 것이었습니다.</p><ul><li>자동으로 <code>CHANGELOG</code> 남기는 방법에 대해 알려주고 있습니다.<ul><li><a href="https://musma.github.io/2020/07/27/changelog.html" target="_blank" rel="noopener noreferrer">GitHub 변경 사항을 자동으로 log로 만들고 release 하기</a></li><li><a href="https://github.com/conventional-changelog/standard-version" target="_blank" rel="noopener noreferrer">https://github.com/conventional-changelog/standard-version</a></li></ul></li><li><code>커밋 컨벤션</code> 과 더불어 <code>package</code> 를 사용하지 않는 것과 사용해서 <code>CHANGELOG</code> 를 남기는 방법에 대해 알려주고 있습니다.<ul><li><a href="https://creampuffy.tistory.com/129" target="_blank" rel="noopener noreferrer">우아한테크코스 4기 프리코스 후기 (2) - Commit Log 컨벤션</a></li><li><a href="https://www.npmjs.com/package/generate-changelog" target="_blank" rel="noopener noreferrer">npm: generate-changelog</a></li></ul></li><li><code>CHANGELOG</code> 를 왜 작성해야 되는지 어떤 이점이 있는지 정리한 <code>CHANGELOG</code> 공식 홈페이지 같은 곳입니다.<ul><li><a href="https://keepachangelog.com/ko/1.0.0/" target="_blank" rel="noopener noreferrer">Keep a Changelog</a></li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="github-release-와-changelog-의-차이">GitHub Release 와 CHANGELOG 의 차이<a class="hash-link" href="#github-release-와-changelog-의-차이" title="제목으로 바로 가기">​</a></h2><p>여기서 <code>GitHub Release</code> 와 <code>CHANGELOG</code> 의 차이가 궁금해졌습니다.</p><ul><li><p><code>ChatGPT</code> 의 답변은 아래와 같더군요.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAzUlEQVQYlT2P7W6DMAxF8/6vV21lUBJIIB8E2CYgYVTotnbV/TjyVWIfJ+J3WRDjCO8DrHVcnQuY528sy4p13XAcB8T970TOmUkpIaWMbUv/5LzjPE+IaDKcHxjrAuND5EpnXe8xzT8Qed/RtB2uRQXZaKa6KdSyRa00pNJodQ9BatUYfF4rvnibyNpbD2sDwjC+Gmnq8lGgrBSKssZXKTmXN8XbaIgbySIbg94GxHHGECfEcUKME2dC0M+0sVCtgeksZ3oTYTrH68PT+AA/BCoTH6vnzAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1454" height="1232"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.0a6112d.1454.png" srcset="/assets/ideal-img/8.0a6112d.1454.png 1454w" width="1454" height="1232"></noscript></div></div></li><li><p>저는 주로 <code>Jest</code> 와 <code>Kubernetes</code> 으로 <code>CHANGELOG</code> 를 살펴봅니다. <code>Jest</code> 의 경우 어떻게 되는지 살펴보았는데 <code>GitHub Release</code> 와 <code>CHANGLOG</code> 가 동일한 것을 볼 수 있었습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAbUlEQVQImWWNUQrCMBAFc/876SHEG6i0oEZNdrNpkmaKrR9CBx7zM/DccDpwPx/50vu8ek/HiSjJMq01Sil/q0w/lzpv4eV6w/sXZoaqoppIZogIUZS3VlzOmWEceTw9MQohxC0IgY8YOvX1fAGn55rvB7GL1AAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1188" height="482"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9.d7305a4.1188.png" srcset="/assets/ideal-img/9.d7305a4.1188.png 1188w" width="1188" height="482"></noscript></div></div><blockquote><p><a href="https://github.com/jestjs/jest/blob/main/CHANGELOG.md#2931" target="_blank" rel="noopener noreferrer">https://github.com/jestjs/jest/blob/main/CHANGELOG.md#2931</a></p></blockquote><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAcklEQVQImXWOwQ7DMAhD+f/v3KY1hzZLCluBxBVUPQ7pCYOwMNXOWLeKqDnnX6huK0opUDWYGb46cahBVXMOQhMzw93T5T5giSex9zGyU+sdj+cLn9bAIghj0HaBHCMCZSwSEbyXkoeh43hnAf+uL3fGE2H7w26a6UqtAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1556" height="812"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/10.c067631.1556.png" srcset="/assets/ideal-img/10.c067631.1556.png 1556w" width="1556" height="812"></noscript></div></div><blockquote><p><a href="https://github.com/jestjs/jest/releases/tag/v29.3.1" target="_blank" rel="noopener noreferrer">https://github.com/jestjs/jest/releases/tag/v29.3.1</a></p></blockquote></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="prettier-vscode-releases-및-changelog-살펴보기">prettier-vscode releases 및 <code>CHANGELOG</code> 살펴보기<a class="hash-link" href="#prettier-vscode-releases-및-changelog-살펴보기" title="제목으로 바로 가기">​</a></h2><p>이번에도 오픈소스의 실 사례를 찾아보자. 이번 내용을 통해 오픈소스가 어떻게 릴리즈 하는가도 볼 수 있다.</p><ol><li><p><code>VSCode 1.79</code> 부터 <code>Prettier</code> 확장에서 에러가 발생하여 아래의 <code>issue</code> 를 찾을 수 있었습니다.</p><ul><li><a href="https://github.com/prettier/prettier-vscode/issues/3020#issuecomment-1586435684" target="_blank" rel="noopener noreferrer">Prettier- TypeError: Cannot read properties of undefined (reading 'uid') · Issue #3020 · prettier/prettier-vscode</a></li></ul></li><li><p>시간이 지나 해당 문제를 <code>Fix</code> 하는 아래의 <code>PR</code> (<a href="https://github.com/prettier/prettier-vscode/pull/3030" target="_blank" rel="noopener noreferrer">https://github.com/prettier/prettier-vscode/pull/3030</a>) 이 올라왔는데</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAR0lEQVQImQXBiRGAIAwAQStglPBGQV777/DcPcLdOZ0iOohl43RgU0Nyxz+LWD/CXBxGMuaK2FDw2nHpRVIl7IWfE9cbtlR+Dr4aDj8aMC4AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2454" height="651"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/11.150d1f8.2454.png" srcset="/assets/ideal-img/11.150d1f8.2454.png 2454w" width="2454" height="651"></noscript></div></div><ol><li>어떤 <code>issue</code> 를 해결하는지 <code>descript</code> 에 서술해 놓았고</li><li><code>Release Note</code> 에 작성할 단위의 <code>Fix</code> 이므로 <code>CHANGELOG.md</code> 에 작성은 하는데 <code>##</code> 를 <code>Unreleased</code> 으로 작성하더군요</li><li><code>Conventional Commits</code> 에서는 컨벤션을 지켜서 자동으로 하던데 그렇지 않은 오픈소스도 만나볼 수 있었습니다.</li></ol></li><li><p>그리고 시간이 조금 지나 위에서 <code>Fix</code> 된 것에 대한 <code>Release</code> 를 위해 <code>CHANGELOG.md</code> 와 <code>프로그램 버전명</code> 을 바꾸는 <code>commit</code> 이 들어왔습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAXElEQVQImR2MSw6AIBBDOYD4QR1+A6KIC73/+WqYxUub5qUqHg9K+0ChYFoDhpmgl46VbizDpQrluSLkhoUYhljk0ThBGydiP1GWK3J94dItULyw+YLdF0mKp2w/FQQmsH32cIYAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2158" height="745"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/12.7f72de0.2158.png" srcset="/assets/ideal-img/12.7f72de0.2158.png 2158w" width="2158" height="745"></noscript></div></div><blockquote><p><a href="https://github.com/prettier/prettier-vscode/commit/4c26721421a4989d6308e52d37118320b4f9c117" target="_blank" rel="noopener noreferrer">https://github.com/prettier/prettier-vscode/commit/4c26721421a4989d6308e52d37118320b4f9c117</a></p></blockquote><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAeklEQVQImQXBixGDIBBAQQrQDPI74RAVNM6Y/ut72TVST2Leke0m6cDJgZMdtx4ssmNTI5SOsaEw20SUhm4XUk7i2vB6EGsn6MmyNsxkE9MixPqQjxefL9za8TqI24MrA18GZl4SsxWidur1kvTGeiXsnfz94XTwiZU/WuEz1Tw9IIsAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2450" height="993"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/13.3226277.2450.png" srcset="/assets/ideal-img/13.3226277.2450.png 2450w" width="2450" height="993"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAT0lEQVQImW3MwQqAMAwD0N3FbZ2061gHooL//4WR9SAIHkIuLwk6TsSiqHYgS8dK9SeCQGIO2W5k2b+gqHfaGsKS2BelXYg8obxwHswQdzy/aiTp0dsXjwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1991" height="689"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/14.a39cd82.1991.png" srcset="/assets/ideal-img/14.a39cd82.1991.png 1991w" width="1991" height="689"></noscript></div></div><blockquote><p><a href="https://github.com/prettier/prettier-vscode/releases/tag/v9.14.0" target="_blank" rel="noopener noreferrer">https://github.com/prettier/prettier-vscode/releases/tag/v9.14.0</a></p></blockquote></li><li><p>그리고 <code>Release</code> 가 이뤄졌는데 눈여겨볼 점은 위에서 <a href="https://github.com/prettier/prettier-vscode/commit/4c26721421a4989d6308e52d37118320b4f9c117" target="_blank" rel="noopener noreferrer"><code>9.14.0</code></a> 커밋까지가 아닌 마지막 커밋까지 포함해서 릴리즈 했다는 것입니다.</p><ol><li><p><code>릴리즈</code> 된 <code>커밋</code> 을 까보면 알 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAgklEQVQImWWOwQ6CMBiDdzEuAuMfjI3BBCPGwElPvv+bfQbRxIRD06Rf01SJ6zHWozPBxZHl8eK2PDF15KANx5OgM4tqzxPSdOQSCGkiTTMuXijrSGEDpmqpfELV7Yj16ROYaoOF9V/flItHuf6OhOsO/tZWF9ehrB8om2FX+td64w22Nz3VUDAtrQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2162" height="1026"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/15.72ebad7.2162.png" srcset="/assets/ideal-img/15.72ebad7.2162.png 2162w" width="2162" height="1026"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAcElEQVQImT3D7QqDIBiAUf9vKz/S1FdlWcHG7v/6nsBBB47KdcfHhgtlNC4x2Xifl4z2girS2Y4fpZ2UeiDS8Xm7B+mjyqXz3r+secMsgraJ2f1PNmG84GJD5XZS+we3ViaXeOowvnTgoT12raR6cAFPSzNTsJiVMgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2171" height="931"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/16.f049dfc.2171.png" srcset="/assets/ideal-img/16.f049dfc.2171.png 2171w" width="2171" height="931"></noscript></div></div><blockquote><p><a href="https://github.com/prettier/prettier-vscode/commit/27a6896e3b31e3a319200d7a799b667d35146b3d" target="_blank" rel="noopener noreferrer">https://github.com/prettier/prettier-vscode/commit/27a6896e3b31e3a319200d7a799b667d35146b3d</a></p></blockquote></li></ol></li></ol><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="결론">결론<a class="hash-link" href="#결론" title="제목으로 바로 가기">​</a></h2><ul><li>오픈 소스에서는 커밋 컨벤션을 잘 지켜서 관리되고 있구나</li><li><code>CHANGELOG</code> 를 사용해서 <code>Release</code> 를 관리를 다들 하는구나<ul><li>다만, 자동화가 아닌 사람이 직접 관리하는 Case도 있구나</li></ul></li><li><code>CHANGELOG</code> 는 사람들이 이해할 수 있는 단위로 작성되는구나<ul><li>이번 예제에서 테스트 환경 등과 같은 부가적인 것도 들어갔는데 <code>CHANGELOG</code> 에 명시하지 않았지만 같이 <code>Release</code> 되었다는 것이 인상 깊었다.</li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="insight">Insight<a class="hash-link" href="#insight" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="git-log-보기-편하게-출력하는-방법"><code>git log</code> 보기 편하게 출력하는 방법<a class="hash-link" href="#git-log-보기-편하게-출력하는-방법" title="제목으로 바로 가기">​</a></h3><ul><li><p><code>git log --pretty="- %s"</code> 입니다.</p></li><li><p><code>git log --pretty="- %s" &gt; CHANGELOG.md</code> 을 하면 간단하게 <code>CHANGELOG</code> 를 뽑아낼 수 있습니다.</p><div class="language-bash codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-bash codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">git</span><span class="token plain"> log --pretty</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"- %s"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> CHANGELOG.md</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- fix: </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> utf-8 encoding option to build.gradle</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- test: </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> </span><span class="token builtin class-name">test</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> judging by user input</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- style: </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> new line at the end of QuizMakerTest.java</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- test: </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> </span><span class="token builtin class-name">test</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> creating new quiz</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- refactor: change class name AnswerBall to Answer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- feat: compose features</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- feat: </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> to repeat or not when it is finished</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- feat: </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> to print result of user input</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- refactor: </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> vo classes to hand values over</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- feat: </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> to check </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> user input is correct</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- feat: </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> to </span><span class="token builtin class-name">read</span><span class="token plain"> and validate user input</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- feat: </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> to create a quiz</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- docs: </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> list to implement</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- feat: setup baseball game project</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="reference">Reference<a class="hash-link" href="#reference" title="제목으로 바로 가기">​</a></h2><ul><li><a href="https://yozm.wishket.com/magazine/detail/1974/" target="_blank" rel="noopener noreferrer">Git 커밋 메시지 컨벤션은 왜 중요할까? | 요즘IT</a></li></ul>]]></content>
        <category label="Git" term="Git"/>
        <category label="협업" term="협업"/>
        <category label="CHANGELOG" term="CHANGELOG"/>
        <category label="오픈 소스" term="오픈 소스"/>
        <category label="Release" term="Release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[트랜잭션 격리 수준 분석: Prisma를 활용한 실습 가이드]]></title>
        <id>/2023/05/28/transaction-isolation-prisma-guide</id>
        <link href="https://parkgang.github.io/blog/2023/05/28/transaction-isolation-prisma-guide"/>
        <updated>2023-05-28T11:00:00.000Z</updated>
        <summary type="html"><![CDATA[트랜잭션 격리 수준 을 Prisma 를 통해서 실제로 어떻게 동작하는지 확인해 보자!]]></summary>
        <content type="html"><![CDATA[<p><code>트랜잭션 격리 수준</code> 을 <code>Prisma</code> 를 통해서 실제로 어떻게 동작하는지 확인해 보자!</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="개요">개요<a class="hash-link" href="#개요" title="제목으로 바로 가기">​</a></h2><p><code>RDBMS</code> 에서 중요한 <code>개념</code> 으로 <code>트랜잭션</code> 이 있습니다.</p><p><code>트랜잭션</code> 은 <code>데이터베이스</code> 의 <code>무결성</code> 을 유지하는 데 중요한 역할을 하며, 복잡한 데이터 작업을 안전하고 일관되게 처리하는 데 사용될 수 있습니다.</p><p>근데 무심코 사용하던 <code>트랜잭션</code> 에도 <code>격리 수준(isolation level)</code> 이 있다는 것을 아시나요? 저는 <code>동시성</code> 이 있는 데이터를 어떻게 안전하게 처리할 수 있는지, <code>Lock</code> 말고는 방법이 없는지 찾아보다가 알게 되었습니다.</p><p>이 <code>격리 수준</code> 을 이용하여 <code>성능</code> 과 <code>데이터 일관성</code> 사이의 <code>트레이드 오프</code> 를 다룰 수 있습니다.</p><p><code>높은 격리 수준</code> 은 더 일관된 데이터를 제공하지만 <code>동시성</code> 이 <code>저하</code> 될 수 있고, <code>낮은 격리 수준</code> 은 <code>더 높은 동시성</code> 을 제공하지만 <code>데이터 무결성 문제</code> 가 발생할 수 있습니다.</p><p>그래서 각 <code>격리 수준</code> 을 이해하고 사용할 수 있어야 하는데 <code>이론</code> 도 중요하지만 실제로 어떻게 동작되는지를 보고 싶었습니다.</p><p>이에 따라 각 <code>격리 수준</code> 에 대한 실제 동작을 <code>TS</code> + <code>Prisma</code> 를 통해 만들어 보았습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="격리-수준">격리 수준<a class="hash-link" href="#격리-수준" title="제목으로 바로 가기">​</a></h2><p>수행 동작을 보기 앞서 <code>SQL 표준</code> 에서 정의한 주요 <code>격리 수준</code> 의 동작에 대해서 정리하고 가겠습니다.</p><p><code>DBMS</code> 는 <code>SQL Server</code> (MS SQL) 을 기준으로 작성했습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="read-uncommitted">Read Uncommitted<a class="hash-link" href="#read-uncommitted" title="제목으로 바로 가기">​</a></h3><p>가장 <code>낮은</code> 격리 수준으로, 다른 트랜잭션에서 커밋 되지 않은 변경사항을 읽을 수 있습니다.</p><p>이 수준에서는 <code>Dirty Read</code> 가 발생할 수 있습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="read-committed">Read Committed<a class="hash-link" href="#read-committed" title="제목으로 바로 가기">​</a></h3><p>대부분의 데이터베이스 시스템에서 기본으로 사용되는 격리 수준입니다.</p><p>커밋 된 데이터만 읽을 수 있으므로 <code>Dirty Read</code> 는 방지되지만, <code>Non-Repeatable Read</code> 는 발생할 수 있습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="repeatable-read">Repeatable Read<a class="hash-link" href="#repeatable-read" title="제목으로 바로 가기">​</a></h3><p>한 트랜잭션이 시작된 후 읽은 데이터는 트랜잭션이 종료될 때까지 다른 트랜잭션에 의해 변경되지 않습니다.</p><p>이로 인해 <code>Non-Repeatable Read</code> 는 방지되지만, <code>Phantom Read</code> 는 여전히 발생할 수 있습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="serializable">Serializable<a class="hash-link" href="#serializable" title="제목으로 바로 가기">​</a></h3><p>가장 높은 격리 수준으로, 트랜잭션이 마치 순차적으로 실행되는 것처럼 보장합니다.</p><p>이 수준에서는 <code>모든 읽기 문제</code> 가 <code>방지</code> 되지만, <code>성능 저하</code> 의 가능성이 가장 높습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="격리-수준에-따라-발생할-수-있는-문제">격리 수준에 따라 발생할 수 있는 문제<a class="hash-link" href="#격리-수준에-따라-발생할-수-있는-문제" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="더티-리드-dirty-read">더티 리드 (Dirty Read)<a class="hash-link" href="#더티-리드-dirty-read" title="제목으로 바로 가기">​</a></h3><p>특정 <code>트랜잭션</code> 에 의해 데이터가 변경되었지만, 아직 커밋 되지 않은 상황에서 다른 트랜잭션이 해당 변경 사항을 조회할 수 있는 문제를 말합니다.</p><p>이 문제는 <code>트랜잭션 A</code> 가 데이터를 변경하고 커밋 하지 않은 시점에 <code>트랜잭션 B</code> 가 변경된 데이터를 읽어온 상황에서, <code>트랜잭션 A</code> 가 변경 내용을 커밋 하지 않고 <code>롤백</code> 한 상황에서 치명적입니다.</p><p>이렇게 되면 <code>트랜잭션 B</code> 는 <code>무효가 된 값</code> 을 읽고 처리하므로 문제가 발생합니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="반복-불가능한-조회-non-repeatable-read">반복 불가능한 조회 (Non-Repeatable Read)<a class="hash-link" href="#반복-불가능한-조회-non-repeatable-read" title="제목으로 바로 가기">​</a></h3><p>같은 트랜잭션 내에서 같은 데이터를 여러 번 조회했을 때 읽어온 데이터가 다른 경우를 의미합니다.</p><p>한 트랜잭션이 동일한 데이터를 여러 번 조회했을 때, 각 조회 사이에 다른 트랜잭션이 데이터를 변경하면 일관성 없는 결과를 얻게 되는데 이는 특히 데이터 분석이나 보고서 생성과 같은 작업에서 큰 문제가 될 수 있습니다.</p><p>이는 읽은 데이터에 대해서 수정(삭제 포함)을 막지 않기 때문에 발생하며 트랜잭션 세션 안에서 컨디션에 따라 조회된 데이터가 다를 수 있음으로 레이스 컨디션이 발생할 수 있습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="가상-읽기-phantom-reads-팬텀-리드">가상 읽기 (Phantom reads, 팬텀 리드)<a class="hash-link" href="#가상-읽기-phantom-reads-팬텀-리드" title="제목으로 바로 가기">​</a></h3><p><code>Repeatable Read</code> 격리 수준인 경우 읽은 데이터에 대해서는 <code>데이터 변경(삭제 포함)</code> 을 <code>방지</code> 하지만 새로운 데이터의 <code>삽입</code> 으로 인한 문제가 발생할 수 있습니다.</p><p><code>ChatGPT</code> 의 말을 인용하자면 아래와 같습니다.</p><div class="codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-text codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">"Phantom Reads"는 데이터베이스 트랜잭션에서 발생할 수 있는 문제 중 하나입니다. 이 문제는 일반적으로 트랜잭션이 동일한 쿼리를 두 번 실행할 때, 첫 번째 쿼리 실행 후 두 번째 쿼리 실행 전에 다른 트랜잭션이 새로운 레코드를 삽입하거나 삭제하는 경우 발생합니다.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">예를 들어, 트랜잭션이 데이터베이스에서 특정 조건에 맞는 레코드를 모두 찾는 쿼리를 실행한다고 가정해 보겠습니다. 이 트랜잭션이 처음에는 100개의 레코드를 찾았다고 합시다. 그런데 이 트랜잭션이 동일한 쿼리를 다시 실행하면, 동일한 조건에 따라 이번에는 101개의 레코드를 찾을 수 있습니다. 이 새로운 레코드(즉, "phantom" 레코드)는 첫 번째 쿼리 실행 후 두 번째 쿼리 실행 전에 다른 트랜잭션에 의해 삽입되었습니다.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">이런 현상은 데이터베이스에서 읽기 일관성(read consistency)을 유지하는 데 문제를 일으킬 수 있습니다. 트랜잭션은 일관성 있는 데이터 집합을 바탕으로 작업을 수행하려고 하는데, 이러한 "phantom" 레코드 때문에 트랜잭션의 결과가 예기치 않게 변할 수 있기 때문입니다.</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="실습-격리-수준-차이-확인-">실습: 격리 수준 차이 확인 ✨<a class="hash-link" href="#실습-격리-수준-차이-확인-" title="제목으로 바로 가기">​</a></h2><p>코드는 <a href="https://github.com/parkgang/prisma-transaction-isolation-level-examples" target="_blank" rel="noopener noreferrer">parkgang/prisma-transaction-isolation-level-examples</a> 에 존재합니다.</p><p>각각의 <code>격리 수준</code> 에 대한 차이를 느끼려면 <code>낮은 격리 수준</code> 에서 <code>높은 격리 수준</code> 으로 올라갈 때 어떤 현상하는지 보면 됩니다.</p><p>그래서 <code>샘플</code> 도 각각의 <code>격리 수준</code> 에 따라서 발생한 문제에 대해서 <code>파일 단위</code> 로 분리했으니 편하게 살펴보실 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAmUlEQVQImVWOyQ7DIAxE8/9/mB67gDGQhCUQkpap4raHWhrZh6fnGaZccCGP2/UOww7zEiTWebD18NMsGY4XMNoVs/cgw3gohRAjUs6I504JMSYM7dlx4YTJOzBbEBmEENBaQ60VvxFw5AzHLEZmhiaCMQylNEopH3D7gkZrsZ2A1iT3+WHfj3+QSUu/JQSx5LyibptAvXe8AWd65Gh0ZQvlAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="670" height="440"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.edf9adb.670.png" srcset="/assets/ideal-img/1.edf9adb.670.png 670w" width="670" height="440"></noscript></div></div><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>아참, 그리고 <code>RDBMS</code> 마다 <code>격리 수준</code> 이 조금씩 다르니 이점을 유의해서 봐주세요.<br>
<code>ORM</code> 을 사용하니까 <code>Docker Image</code> 만 바꾸면 손쉽게 테스트해 보실 수 있을 겁니다. ✌️</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="insight">Insight<a class="hash-link" href="#insight" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="repeatable-read-걸리면-트랜잭션-이-끝날-때까지-다른-작업이-대기함"><code>Repeatable Read</code> 걸리면 <code>트랜잭션</code> 이 끝날 때까지 다른 작업이 대기함<a class="hash-link" href="#repeatable-read-걸리면-트랜잭션-이-끝날-때까지-다른-작업이-대기함" title="제목으로 바로 가기">​</a></h3><p><code>Repeatable Read</code> 격리 수준의 트랜잭션이 시작하자마자 <code>Select</code> 쿼리를 넣으니까 동작이 달라지는 것을 확인했습니다.</p><p>원래는 <code>비동기 작업</code> 에서 쿼리 시작 후 바로 수행이 완료되었다는 <code>console.log</code> 가 찍혔는데 <code>Repeatable Read</code> 트랜잭션 시작하자마자 <code>Select</code> 쿼리를 추가하니 비동기 작업의 쿼리가 <code>Repeatable Read</code> 의 트랜잭션이 끝날 때까지 수행이 안되고 있는 것을 볼 수 있었습니다.</p><p>생각을 해보니까 <code>읽은</code> 데이터에 대해서 <code>lock</code> 을 걸어서 <code>격리</code> 시킨다는 말이 떠올랐고 <code>Repeatable Read</code> 트랜잭션 시작하자마다 쿼리를 수행하니 해당 <code>row</code> 가 <code>lock</code> 이 걸린 것이고 그래서 비동기 작업에서 해당 <code>row</code> 에 대해서 계속 수정을 하고 있지 않아서 대기 중이었던 것으로 알게 되었습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="트랜잭션-이-시작되자마자-트랜잭션-이-끝날-때까지-락이-걸리는-것이-아니었다"><code>트랜잭션</code> 이 시작되자마자 <code>트랜잭션</code> 이 끝날 때까지 락이 걸리는 것이 아니었다<a class="hash-link" href="#트랜잭션-이-시작되자마자-트랜잭션-이-끝날-때까지-락이-걸리는-것이-아니었다" title="제목으로 바로 가기">​</a></h3><p><code>트랜잭션</code> 이 시작되면 해당 <code>세션</code> 동안은 <code>격리 수준</code> 에 맞게 <code>락</code> 과 같은 동작이 되는 줄 알았습니다.</p><p>근데 실제로 그렇지 않았고 <code>조회</code> 를 해야지 해당 <code>row</code> 에 대해서 <code>락</code> 이 걸리더군요.</p><p>생각을 해보니 당연한 원리 같긴 합니다. 제가 이전에 생각한 대로 이면 트랜잭션 시작하자마다 모든 <code>Table lock</code> 이랑 다를 게 없기 때문입니다.</p><p>해당 동작은 <code>Repeatable Read</code> 일 때와 아닐 때의 동작이 어떻게 다른지 확인하면 차이를 알 수 있을 것입니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="read-committed-인-경우-조회-시-commit-를-바라봐야-하는-트랜잭션-이-끝날-때까지-대기한다"><code>Read Committed</code> 인 경우 조회 시 <code>Commit</code> 를 바라봐야 하는 <code>트랜잭션</code> 이 끝날 때까지 대기한다<a class="hash-link" href="#read-committed-인-경우-조회-시-commit-를-바라봐야-하는-트랜잭션-이-끝날-때까지-대기한다" title="제목으로 바로 가기">​</a></h3><p><strong><code>Read Committed</code></strong> 인 경우 조회 시 <code>Commit</code> 이전으로 가져오는 줄 알았는데 <code>Commit</code> 를 바라봐야 하는 트랜잭션이 끝날 때까지 대기하는 것을 볼 수 있었습니다.</p><p>근데 해당 동작은 <code>DBMS</code> 혹은 <code>ORM Client</code> 마다 다를 수 있다는 생각이 들었습니다.</p><p>무한정 대기하기에는 <code>성능 이슈</code> 가 크고 굳이 기다리지 않고 내부적으로 처리할 수 있는 방법이 있어 보이기 때문입니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="serializable-는-select-한-테이블-단위-가-아닌-데이터-에-대해서만-생성을-포함한-변경을-막는다"><code>Serializable</code> 는 <code>Select</code> 한 <code>테이블 단위</code> 가 아닌 <code>데이터</code> 에 대해서만 생성을 포함한 변경을 막는다<a class="hash-link" href="#serializable-는-select-한-테이블-단위-가-아닌-데이터-에-대해서만-생성을-포함한-변경을-막는다" title="제목으로 바로 가기">​</a></h3><p><code>Serializable</code> 격리 수준인 경우 <code>Select</code> 시 해당 테이블에 대해서 모두 <code>생성</code> 이 불가능한 것으로 알고 있었습니다.</p><p>하지만, 실제로 해보니 그렇지 않았으며 <code>Select</code> 한 데이터에 대해서만 <code>수정</code> 이 안 되는 것이며 만약, <code>select * from</code> 으로 <code>모두 Select</code> 한다면 해당 테이블에 대해서 생성이 불가능하도록 바뀌는 것이었습니다.</p><p>고로, <code>Repeatable Read</code> 와 큰 차이는</p><ul><li><code>Repeatable Read</code> 은 Select 여부와 무관하게 생성된 데이터가 조회된다는 것이고</li><li><code>Serializable</code> 는 Select 한 데이터에 대해서는 다시 조회하더라도 <code>Phantom reads</code> 가 발생하지 않도록 해줍니다.</li><li>사실 <code>Select all</code> 말고 든 <code>Serializable</code> 와 <code>Repeatable Read</code> 의 차이를 증명할 수 있나 싶습니다.</li></ul><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="repeatable-read-와-serializable-의-차이"><code>Repeatable Read</code> 와 <code>Serializable</code> 의 차이<a class="hash-link" href="#repeatable-read-와-serializable-의-차이" title="제목으로 바로 가기">​</a></h3><p><code>Repeatable Read</code> 는 <code>Serializable</code> 과는 달리 <code>팬텀 읽기</code> 를 방지하지 않습니다.</p><p><code>Repeatable Read</code> 는 새로운 데이터의 삽입은 막지 않습니다. 그래서 <code>팬텀 읽기</code> 가 발생할 수 있습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="트랜잭션-격리-수준은-읽기와-관련이-있다">트랜잭션 격리 수준은 읽기와 관련이 있다<a class="hash-link" href="#트랜잭션-격리-수준은-읽기와-관련이-있다" title="제목으로 바로 가기">​</a></h3><p><code>SQL Server</code> 의 <a href="https://learn.microsoft.com/ko-kr/sql/t-sql/statements/set-transaction-isolation-level-transact-sql?view=sql-server-ver15#remarks" target="_blank" rel="noopener noreferrer">learn.microsoft.com의 SQL Server 문서</a> 를 알게 된 것으로 <code>Read Committed</code> 와 <code>Repeatable Read</code> 는 <code>row</code> 단위 락이라고 나와서 명확하게 설명해 줘서 좋았습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAPklEQVQImT2MQQoAMQjE+v9vVpQVhY4+YBYLu4ccckiWmjEzGRE8AKuKAH7Gu5sr3GkiVNl8TJnuNxpm8E1eGJdLaaWVxfYAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1748" height="416"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.b851160.1748.png" srcset="/assets/ideal-img/2.b851160.1748.png 1748w" width="1748" height="416"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p><code>이론</code> 이 아닌 실제 <code>격리 수준</code> 에 따른 동작을 보니 더 이해도 쉬워지고 오해하고 몰랐던 부분도 알게 되어서 좋았습니다.</p><p><code>트랜잭션</code> 을 꼭 <code>롤백</code> 단위로만 생각했는데 이번에 배우면서 그런 것이 아니라는 것을 느낄 수 있었고 트랜잭션의 <code>ACID</code> 에 대해서 생각해 보고 공부해야겠다는 생각을 해볼 수 있었습니다.</p><p>그리고 <code>트랜잭션 in 트랜잭션</code> 이면 또 여러 상황을 고려해야 하므로 알아야 할 것이 많습니다.</p><p>저와 같은 가려움을 가지고 계셨던 분들이 해당 들을 통해 시원해졌으면 좋겠습니다.</p><p>읽어주셔서 감사합니다.</p>]]></content>
        <category label="Prisma" term="Prisma"/>
        <category label="RDBMS" term="RDBMS"/>
        <category label="Transaction" term="Transaction"/>
        <category label="Transaction Isolation Level" term="Transaction Isolation Level"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Prisma로 멀티 스키마 지정, 멀티 데이터베이스 연결 방법]]></title>
        <id>/2023/05/22/prisma-multi-schemas-and-multi-connections</id>
        <link href="https://parkgang.github.io/blog/2023/05/22/prisma-multi-schemas-and-multi-connections"/>
        <updated>2023-05-22T11:00:00.000Z</updated>
        <summary type="html"><![CDATA[Prisma 으로 멀티 테넌트(Multi-tenant) 구조를 위한 멀티 스키마(Multiple schemas) , 멀티 데이터베이스 연결(Multi-database connection) 방법을 소개합니다.]]></summary>
        <content type="html"><![CDATA[<p><code>Prisma</code> 으로 <code>멀티 테넌트(Multi-tenant)</code> 구조를 위한 <code>멀티 스키마(Multiple schemas)</code> , <code>멀티 데이터베이스 연결(Multi-database connection)</code> 방법을 소개합니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="개요">개요<a class="hash-link" href="#개요" title="제목으로 바로 가기">​</a></h2><p>프로젝트 진행 중 <code>SaaS</code> 형 <code>DB</code> 설계를 위해서 <code>멀티 테넌트(Multi-tenant)</code> 구조가 필요하여 <code>멀티 데이터베이스 연결(Multi-database connection)</code> 과 함께 <code>멀티 스키마(Multiple schemas)</code> 지원이 필요하였습니다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>여기서 멀티 테넌트(Multi-tenant) 란?</h5></div><div class="admonition-content"><p>하나의 <code>애플리케이션</code> 또는 <code>소프트웨어 인스턴스</code> 가 여러 사용자 또는 <code>테넌트(tenant)</code> 를 지원하는 아키텍처를 지칭합니다.</p><p>이 용어는 주로 <code>클라우드 컴퓨팅</code> , <code>SaaS(Software as a Service)</code> 모델 또는 <code>대규모 엔터프라이즈 애플리케이션</code> 에서 사용되며, 여러 고객이나 사용자 그룹이 동일한 애플리케이션 인스턴스를 공유하되, 각각의 데이터와 구성이 격리되어 있는 환경을 의미합니다.</p></div></div><p>아래의 요구 사항을 충족할 수 있어야 했습니다.</p><ul><li><code>커넥션</code> 을 <code>동적</code> 으로 변경할 수 있어야 합니다.</li><li><code>커넥션</code> 과 관계없이 <code>원하는 스키마를 지정</code> 해서 <code>쿼리</code> 할 수 있어야 합니다.</li></ul><p>프로젝트에서 <code>Prisma</code> 를 사용하고 있었는데 <code>Prisma</code> 은 선언적으로 정의된 <code>Schema</code> 를 기준으로 <code>Client</code> 가 생성되기 때문에 여러 개의 <code>Schema</code> 와 <code>Connection</code> 을 가질 수 있는지 의문이었고 이에 대해 찾아보게 되었습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="멀티-지정-방법을-지원하는가">멀티 지정 방법을 지원하는가?<a class="hash-link" href="#멀티-지정-방법을-지원하는가" title="제목으로 바로 가기">​</a></h2><p>찾아보니 3가지 선택지가 있더군요.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="third-party-prisma-multi-tenant">Third Party: prisma-multi-tenant<a class="hash-link" href="#third-party-prisma-multi-tenant" title="제목으로 바로 가기">​</a></h3><p><a href="https://github.com/errorname/prisma-multi-tenant" target="_blank" rel="noopener noreferrer">https://github.com/errorname/prisma-multi-tenant</a> 를 찾을 수 있었는데 여러 개의 <code>커넥션</code> 을 가질 수 있긴 한데 <code>스키마</code> 가 <code>고정</code> 되어야 한다고 합니다.</p><p>즉, 같은 스키마인데 DB가 여러 개인 경우 사용될 수 있을 것인데 제 요구 상황과 다름으로 배제하였습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="prisma-공식-지원">Prisma 공식 지원<a class="hash-link" href="#prisma-공식-지원" title="제목으로 바로 가기">​</a></h3><p><a href="https://www.prisma.io/docs/orm/prisma-schema/data-model/multi-schema" target="_blank" rel="noopener noreferrer">Prisma with multiple database schemas</a> 라는 이름으로 <code>Prisma</code> 공식 문서에 있더군요.</p><p>테스트는 해보지 않았지만 <code>Prisma</code> 에서 <code>멀티 스키마</code> 를 지원하려고 하는 거 같습니다.</p><p>근데 문서를 보면 동적으로 커넥션 정보를 바꾸고 싶은데 그런 것이 가능할지 의문이었습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="여러-개의-schema를-정의하고-client-generator-하기">여러 개의 Schema를 정의하고 Client generator 하기<a class="hash-link" href="#여러-개의-schema를-정의하고-client-generator-하기" title="제목으로 바로 가기">​</a></h3><p><code>Prisma multiple connections</code> 라고 <code>구글링</code> 하여 <a href="https://github.com/prisma/prisma/issues/2443#issuecomment-630679118" target="_blank" rel="noopener noreferrer">https://github.com/prisma/prisma/issues/2443#issuecomment-630679118</a> 방법을 찾을 수 있었습니다.</p><p>여러 개의 <code>Schema</code> 를 정의하고 <code>Client generator</code> 하는 방법이었는데 <code>Prisma</code> 의 기본 메커니즘을 따라 하는 것이며 동작 방식이 명확해서 이것을 사용하기로 하였습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="멀티-지정-방법">멀티 지정 방법<a class="hash-link" href="#멀티-지정-방법" title="제목으로 바로 가기">​</a></h2><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>구현체는 코드는 <a href="https://github.com/parkgang/prisma-multi-schemas-and-multi-connections" target="_blank" rel="noopener noreferrer">parkgang/prisma-multi-schemas-and-multi-connections</a> 에 존재합니다.</p></div></div><p><code>README</code> 에 지정 방법을 작성했으니 해당 방법을 보는 것이 도움이 되실 것입니다. 해당 글에서는 <code>핵심</code> 과 <code>주의점</code> 에 대해서 서술하겠습니다.</p><ul><li><p><code>multiple schemas</code> 시나리오이기 때문에 <code>*.prisma</code> 는 여러 개 생성되어야 합니다.</p><ul><li><code>*.prisma</code> 단위로 스키마가 정의됩니다.</li><li>스키마가 헷갈리면 <code>DB 단위</code> 라고 생각하세요.</li><li>스키마에는 <code>여러 Table</code> 이 정의될 것입니다.</li></ul></li><li><p><code>스키마</code> 에 맞는 <code>Client</code> 를 호출해야 하는데 공식 문서에는 <code>import { PrismaClient } from '@prisma/client'</code> 으로 <code>Client</code> 가 한 개만 존재할 수 있는 것처럼 생겨서 헷갈릴 수 있는데 현재는 <code>스키마</code> 에 맞게 <code>Client</code> 를 <code>generator</code> 하므로 아래의 방법으로 필요한 <code>Client</code> 를 호출할 수 있습니다.</p><div class="language-ts codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-ts codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> PrismaClient </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> PrismaClientCatalog </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"./prisma/catalog/client"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> PrismaClient </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> PrismaClientTenant </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"./prisma/tenant/client"</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p><code>스키마</code> 에 맞는 <code>커넥션</code> 을 <code>동적</code> 으로 변경할 수 있어야 하는데 아래의 문법으로 변경할 수 있습니다.</p><div class="language-ts codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-ts codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> PrismaClient </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@prisma/client"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> client1 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">PrismaClient</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  datasources</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> db</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> url</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"postgres://localhost/db1"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> client2 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">PrismaClient</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  datasources</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> db</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> url</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"postgres://localhost/db2"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li></ul><p>이렇게 <code>스키마</code> 에 맞는 <code>Client</code> 를 호출해서 원하는 <code>커넥션</code> 을 물려서 사용할 수 있는 환경이 마련되었습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="insight">Insight<a class="hash-link" href="#insight" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="npm-i-prismaclient-가-설치된-상태에서-진행해야-한다"><code>npm i @prisma/client</code> 가 설치된 상태에서 진행해야 한다<a class="hash-link" href="#npm-i-prismaclient-가-설치된-상태에서-진행해야-한다" title="제목으로 바로 가기">​</a></h3><p><code>스키마</code> 에 맞게 <code>Prisma Client generator</code> 을 하니까 굳이 <code>@prisma/client</code> 패키지가 필요한가? 싶을 수 있습니다.</p><p>설치하지 않으면 아래와 같은 에러 메시지와 함께 <code>Prisma Client</code> 가 <code>generator</code> 되지 않고 이상하게 파일이 생성됩니다.</p><div class="codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-text codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">Error: Could not resolve @prisma/client despite the installation that we just tried.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Please try to install it by hand with npm install @prisma/client and rerun prisma generate 🙏.</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAYAAAALpr0TAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA70lEQVQYlVWP206DQBRFBxjmUnCGi6UUKAVq7EUfTEzU+AP+/xctA2pan/Z5WGdlbxHHCebznfgyoVvH6qEk1BYRGQJ5k/ORbTr64zOyvkP6FCE0IjT/YCEiS+JKLi8fpP0GmadESULkUgJ1Y/z7eHx65b4fiHuHnUrMWBD5ZDEH8S8YyIS3rzPj6YAMHUZlaO2xqxJjs58qizE01GNFvlujtjl2V6DXGabJMU2Bqv0VdGvHqvaoLseOBXqbobYeNWflrmBW+QWMuwy9z1FDTtx4hJwXa4SYOwYKUbek1Ybz0HLoG45Ty2nq2Lf1svobiaRo2LKd7qoAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="163" height="154"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.afface9.163.png" srcset="/assets/ideal-img/1.afface9.163.png 163w" width="163" height="154"></noscript></div></div><p>해당 이유는 공식문서 <a href="https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch/relational-databases/install-prisma-client-typescript-sqlserver" target="_blank" rel="noopener noreferrer">https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch/relational-databases/install-prisma-client-typescript-sqlserver</a> 를 보니까 아래의 <code>다이어그램</code> 으로 잘 설명되어 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA1klEQVQImWNwDc1gCo/J5WL4/5/h88aN4r/u3zf+++SF4cfzN4RjLz5gSE8rMwyKK5RhiCnqZHOy9xP8uGeB+v+XV93mTpmds2HGZMf/B+u405IL7bIL6qPCEos1GQzcIhnCAhMZTKr6mIqzKxmcg1J9glJCDfb/Z2CITC0Oj82oigyKzVdn0HUKYwjxj2Pw8UtksvNNYvAKSQ/wTC3QOMzAwOAWku7gE54V5hOepcFQUD8VjL3CMpm8QjMZvEIz/LyTmjS9d/znZP//n8kzssDLMzhVCwDJqk0Qq9zyMAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1600" height="750"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.a0c7761.1600.png" srcset="/assets/ideal-img/2.a0c7761.1600.png 1600w" width="1600" height="750"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="스키마-는-독립적인-디렉터리에서-관리하는-것이-좋다"><code>스키마</code> 는 독립적인 디렉터리에서 관리하는 것이 좋다<a class="hash-link" href="#스키마-는-독립적인-디렉터리에서-관리하는-것이-좋다" title="제목으로 바로 가기">​</a></h3><p>아래와 같이 하나의 디렉터리에서 관리하면 좋지 않다는 것을 의미합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAXklEQVQImWXGQQrCMBQA0U9txdagF6iGLIImDfmgSRcV73+sKYUuBBePGXnqi/L5ElJB3wsxV2IuhFyZdPtK0hmR3nC53TkNV0TaXfPjsPd4xowO6x74qIzW03TDnxWm3CYVfpDiQwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="362" height="109"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.b6e208b.362.png" srcset="/assets/ideal-img/3.b6e208b.362.png 362w" width="362" height="109"></noscript></div></div><p>이유는 아래와 같은데</p><ul><li><p><code>migrate</code> 시 구분이 어렵습니다. 2개의 스키마에 대해서 동시에 진행되기 때문인데 만약, <code>db1</code> , <code>db2</code> 의 스키마 모두 <code>init</code> 이라고 이름을 지정하면 어떻게 구분할 건가요? 시간 차이로 인하여 아래와 같이 생길 텐데 구분이 어렵습니다. 물론 <code>00000000000000_db1_init</code> , <code>00000000000000_db2_init</code> 와 같이 구분하여 이름을 지정할 수 있겠지만 이건 원초적인 해결 방법이 아닙니다.</p><p><code>db1</code> , <code>db2</code> 모두 <code>init</code> 라고 하니까 구분이 어렵습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAOCAYAAAAWo42rAAAACXBIWXMAAAsTAAALEwEAmpwYAAABd0lEQVQokV2R3Y6bQAyF+SdAZgYGSAgTkgAJZFmaSmnV7E170/d/pq8iu1V/Lo4sWbY/H9vyghg9TQR5yrpUeHGM5QQ43uofWZblMZx7zgdDXGbEQuF6MZbtY9neH23qE2/zne9fHjTDyGWe6aaJnWnZ1e17NC3W4TRw/fyNrz9+Mj5mrm83ro8b53mmn17pxukpa0GEkeDl0x2xy5CNJj0VqGOOOmiSShHlAsv1VsTrjOH2QFQaWWvSpkDWOXKnn7lYK6zFkRcKdHdHmYK83yBNjqgy1D5/NnrRYs4J8JUm2xna/sqhHTiPM+35SneZqJsW2w2X8wTUG8lwKVib4omznRDH/f+Ojk8kN2T7I8IohNHEpSTZKpxwmeRje+E72pPLTg2n7sLWHGjHkX3XYY4dZbX//ZmA7VbR9yV5Y8hNhZAFSaKRqmCt8o9C20dlBVXboFpNeiwI0zWrTOAEf6PdAHeVEagNsRZEmSIpFEmZ4kcx9oepX23cqvy+HuDwAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="400" height="532"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.23df937.400.png" srcset="/assets/ideal-img/4.23df937.400.png 400w" width="400" height="532"></noscript></div></div></li><li><p>원초적으로 <code>migrate</code> 시 이전 스키마와 비교하여 sql문이 생성되는데 이전 스키마가 같은 스키마를 기준으로 하지 않기 때문에 아래와 같이 이상한 sql문이 생성됩니다. 이전에 생성된 <code>migrate</code> 의 <code>table</code> 을 <code>drop</code> 하는 코드가 들어가 있습니다.
오른쪽 <code>sql</code> 은 왼쪽 <code>sql</code> 보다 늦게 <code>migrate</code> 으로 생성된 <code>sql</code> 인데 <code>DROP TABLE "User";</code> 이라는 의도하지 않는 쿼리가 있는 것을 확인할 수 있습니다.
이는 이전 <code>migrate</code> 와 <code>diff</code> 하여 생성되었기 때문입니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAQ0lEQVQImS3KUQqAMAwE0R6gmrbYJNJW8f6HHIn48WAZNo154zaRqmylk+VAbWA+2asizT4pDjEihiwdPxfXeih/Cy/cOxiwcnKeEgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3690" height="870"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.2f8088c.3690.png" srcset="/assets/ideal-img/5.2f8088c.3690.png 3690w" width="3690" height="870"></noscript></div></div></li><li><p>큰 문제는 아닌데 스키마 파일이 바뀌기 때문에 <code>CLI</code> 에서 파일 이름을 지정해야 하는 귀찮음이 있습니다.</p><div class="language-bash codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-bash codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">npx prisma migrate dev --name init --schema prisma/db1-schema.prisma</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">npx prisma migrate dev --name init --schema prisma/db2-schema.prisma</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li></ul><p>고로 아래와 같이 독립적인 디렉터리에서 관리해야지 스키마 파일 이름도 깔끔하게 <code>schema.prisma</code> 으로 떨어져서 기본 값으로 지정되어 찾기가 편하고 <code>migrate</code> 시 차곡차곡 지정됩니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAASCAYAAABit09LAAAACXBIWXMAAAsTAAALEwEAmpwYAAABrElEQVQokWWR227bMBBEdbFFipIoUbIk6hbLipLGrdGHAslL//+7TkHaSBv0YbDE7mB3ZhgcZU7740oxNOih5iBSwlgQHeQXBK457W8M+4Lq9BdS+M87CMIjRTMz/vxNmmniWBGEyf8bgyghr3qWt1/kbUU5nZBV4clBkNyrRywQqebt+41m7TGXE/Wlo7DGQzUlsiwI7kIT2u0ds0xU58YTzdrS7L2vR5W50xIhJPvLTDa0VEvDMc04SMUxvSOMhCPeT8/rQjZoclshyoykzLxrN/+MJzhmpP2KXRbm/ZlxW5n3DZlpnNmHa0HVWNrzSDNYqrrH1JbK9CQy/8zS51jbC+PtHalzgtDpiXH9v4ELgjBKUGXHdP0gMxVF25DkOWHkdDmCeJiJJSo3fLveqNfuEUmHqivSukRofY8nOqRe8PjygZkmitFQPbWYc0e99Zhzi9CF25iSJJLzvlNMlmbrSE2J1NpXBy/DuZaqYNsG8sX4jXGqiJXyGTqN/qp3JjTKPmMvM8Pzit2emF4vyLwkjJ1rT5REiUI1LafTjB1Xenum65+Qqnz8TMof6/rRDVbrmMUAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="430" height="752"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.f5ee6ed.430.png" srcset="/assets/ideal-img/6.f5ee6ed.430.png 430w" width="430" height="752"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAASUlEQVQImR3KQQqAIBBAUfeFaY46OiUlQfc/4Q9avN1zfVzYcSOihFhYN2HxiazGfF5qNZIorlSjtUHKnSgNH8svJMXO+cewVz70vxlF4iYAQAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2584" height="488"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.dd7c4ce.2584.png" srcset="/assets/ideal-img/7.dd7c4ce.2584.png 2584w" width="2584" height="488"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>이렇게 <code>Prisma</code> 에서 <code>멀티 스키마(Multiple schemas)</code> , <code>멀티 데이터베이스 연결(Multi-database connection)</code> 방법에 대해서 알아보았습니다.</p><p><code>Prisma</code> <code>Hello, world</code> 에서는 <code>DB 연결</code> 을 위해 따로 지정 없이 종속된 <code>Schema</code> 정보를 이용하길래 <code>멀티 연결</code> 이 가능한가 걱정이 있었는데 이해하고 해보니까 생각보다 손쉽게 구현할 수 있었습니다.</p><p>아쉬운 점으로 <code>Prisma</code> 에서 공식으로 지원하는 것이 아닌 <code>Client generator</code> 방법을 응용한 것이라 <code>업데이트</code> 에 따른 <code>Breaking Changes</code> 가 걱정되는 부분이 있습니다.</p><p>읽어주셔서 감사합니다.</p>]]></content>
        <category label="Prisma" term="Prisma"/>
        <category label="DB" term="DB"/>
        <category label="multi-schemas" term="multi-schemas"/>
        <category label="multi-connections" term="multi-connections"/>
        <category label="multi-tenant" term="multi-tenant"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[레이어 분리와 DDD의 중요성]]></title>
        <id>/2023/04/28/layer-separation-and-ddd</id>
        <link href="https://parkgang.github.io/blog/2023/04/28/layer-separation-and-ddd"/>
        <updated>2023-04-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[프로젝트를 진행하면서 느낀 레이어 분리 와 DDD 의 중요성]]></summary>
        <content type="html"><![CDATA[<p>프로젝트를 진행하면서 느낀 <code>레이어 분리</code> 와 <code>DDD</code> 의 중요성</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="개요">개요<a class="hash-link" href="#개요" title="제목으로 바로 가기">​</a></h2><p>기능을 개발하며 한곳에 엄청난 것을 때려 넣다 보니 수정도 힘들고 무엇보다 리뷰가 너무 힘들었습니다.</p><p>혼자 할 때는 별문제가 없습니다. 사실 혼자 하는 제 자신이 저만의 규칙으로 잘 정리할 거거든요.</p><p>문제는 협업을 하는 순간 저만의 규칙이 아닌 코드가 보이게 되고 협업자와 서로 마이웨이를 가면 코드 리딩이 너무 어려워집니다.</p><p>이를 해결하기 위해서 여러 글들을 읽어보고 느낀 점입니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="협업에-대한-문제">협업에 대한 문제<a class="hash-link" href="#협업에-대한-문제" title="제목으로 바로 가기">​</a></h2><p>혼자 할 때는 규칙이 없어도 내가 생각하는 이상적인 구조로 파일을 배치하고 개발해 나갔으며 리펙토링이 필요한 시점에도 내가 모든 것을 파악하고 있기 때문에 리펙토링에 유연했다.</p><p>협업을 하면서 나와 생각이 다른 엔지니어와 일하게 되었으며 아키텍처를 아무리 설계해도 제 생각과 다른 코드가 생성된다는 것을 경험할 수 있었습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="왜-ddd">왜 DDD?<a class="hash-link" href="#왜-ddd" title="제목으로 바로 가기">​</a></h2><p>그러다 보니 코드 리뷰도 어려워지었고 리펙토링을 하고 싶어도 이해가 안 된 코드도 있고 의도가 모호한 코드와 더불어 수정 시 <code>산탄총 수술(Shotgun Surgery)</code> 과같이 Side Effect가 거대해서 수정하기가 어려웠습니다.</p><p>결국 보면 모호한 규칙과 더불어 App을 만드는데 필요하게 Domain 분리를 안 했기 때문이라고 결론이 나왔습니다.</p><p>저는 처음에 Domain의 단위를 RDBMS의 최소 엔티티 단위인 Table로 생각하고 진행하였습니다.</p><p>그렇게 진행하던 도중 잘못 생각했다는 시점은 생각보다 빨리 만날 수 있었는데 Table이 여러 개의 Table와 릴레이션 된 경우 처리가 복잡해졌습니다. 애초에 Table 단위로 레이어를 분리하여 로직을 작성한다는 것이 어려웠습니다. Table에서 여러 Table을 참조해서 데이터가 가공되어야 하는데 그것이 불가능했기 때문이었습니다.</p><p>결국 생각이 돌고 돌아 PO 및 엔지니어가 더불에 생각하는 처리의 단위가 Domain이라는 것을 알게 되었고 Domain 위주 개발을 위해서 DDD를 도입해야겠다고 생각했습니다.</p><p>저는 처음에 DDD가 대단한 것인 줄 알았는데 공부를 해고 찾아보면 느끼는 점으로 지금 제가 이전에 겪었던 문제로 <code>데이터 주도</code> 개발을 하면서 겪은 문제로 <code>도메인 단위</code> 로 개발하는 것이 가독성 및 기능 개발에 유리하다는 것을 알았습니다.</p><p>MSA를 할 때 Service 단위를 어떻게 찢어내기가 고민인 것처럼 DDD도 어떻게 Domain 단위로 찢어내지가 고민이더군요.</p><p>이에 대한 답은 기능 개발 혹은 스키마를 설계하다 보면 자연스럽게 따라오는 Domain 단위에 찢어낼 수 있었습니다.</p><p>결국 제품의 형태에 맞게 자연스럽게 Domain이 정해진다는 것을 느꼈고 앞으로 Domain은 변경될 것입니다. Domain이 변경되면 그 이유에 맞게 팀과 엔지니어는 이해할 수 있을 것입니다.</p><p>이게 보니까 DDD가 빛을 보려면 제품하는 형태의 특이한 Domain이 있어야 공감이 되면서 분리가 되는 것이라 Todo와 같이 기능 가짓수도 적고 Common 한 Domain에서는 굳이 DDD가 필요 없을 것입니다.</p><p>특이한 Domain을 예시로 글로 설명하기 어려워서 대부분의 인터넷 글을 그나마 Common 하면서 어려운 Domain인 쇼핑물을 예시로 들더군요.</p><p>사실 저는 해당 프로젝트 이전 DDD에 공부할 때는 어려운 Domain을 건드려본 적도 없고 인터넷 대부분의 예시인 쇼핑물을 만들어본 적이 없어서 깊은 공감보다는 가볍게 공감하여 넘어갔는데 실제로 프로젝트를 진행하면서 어려운 Domain과 협업을 하다 보니 왜 필요한지 이해하고 사용할 수 있게 되었습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="커뮤니케이션도-협업이다">커뮤니케이션도 협업이다<a class="hash-link" href="#커뮤니케이션도-협업이다" title="제목으로 바로 가기">​</a></h2><p>그리고 DDD와 더불어 아키텍처 그리고 협업이라는 것이 문서 혹은 팀에 규정된 것이 아니라 모두가 이해하고 생각하는 것이 중요하다는 것도 느낄 수 있었습니다.</p><p><a href="https://yozm.wishket.com/magazine/detail/1884/" target="_blank" rel="noopener noreferrer">소프트웨어 설계 20년 해보고 깨달은 ‘좋은 설계’의 조건</a> 글을 보니까 아키텍처 같은 것이 팀에서 규정한다고 끝이 아니라 계속해서 커뮤니케이션해 나가는 것도 하나의 아키텍처라는 것이 공감되었습니다.</p><p>실제 해보니 아무리 말해도 일정이 밀리고 급해지면 백날 말한 규칙이 다 깨지더군요 결국 처음 개발할 때부터 이해도 있게 규칙에 맞게 개발되어야지 해결될 것이라고도 느낄 수 있었습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="왜-레이어를-분리해야-할까">왜 레이어를 분리해야 할까?<a class="hash-link" href="#왜-레이어를-분리해야-할까" title="제목으로 바로 가기">​</a></h2><p>사실 개발하면서 느낍니다 이거 이렇게 하면 결합도가 너무 커서 확장이 어려울 거 같은데?</p><p>그럼 그때 되면 생각하자~ 하고 넘기는데 이제 넘겼던 그 문제가 발생할 때는 기술 부채로써 너무 고통스럽더군요.</p><p>이걸 방지하려면 단일 책임 원칙으로 레이어를 분리하면 되는 것이라고 생각합니다.</p><p>조금 더 코드 디자인적으로는 순수 함수 형태로 만드는 것이죠.</p><p>제 오판이었던 RDBMS의 최소 엔티티 단위인 Table 단위로 시작해서 FE와 BE의 서로 interface는 같겠지 하고 진행했지만 실제 UI를 그리면서 조금씩 달라지는 스키마를 보며 아… 레이어를 더 빡세게 분리해야겠다고 생각할 수 있었습니다.</p>]]></content>
        <category label="레이어" term="레이어"/>
        <category label="DDD" term="DDD"/>
        <category label="협업" term="협업"/>
        <category label="소프트웨어 아키텍처" term="소프트웨어 아키텍처"/>
        <category label="커뮤니케이션" term="커뮤니케이션"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[parkgang.log(2022)]]></title>
        <id>/2023/03/07/2022-retrospective</id>
        <link href="https://parkgang.github.io/blog/2023/03/07/2022-retrospective"/>
        <updated>2023-03-07T05:46:00.000Z</updated>
        <summary type="html"><![CDATA[벌써 2023년 3월이네요 이제서야 2022년 회고를 작성하다니... 밀린 일이 마무리되고 회고를 작성하려고 하다 보니 끝도 없이 미뤄져서 더 이상은 안되겠다 싶어 늦었지만 이제서야 작성하게 되네요. 어디 한번 2022년은 잘 보냈는지 회고해 보고 앞으로의 2023을 디자인해 볼까요?]]></summary>
        <content type="html"><![CDATA[<p>벌써 2023년 3월이네요 이제서야 2022년 회고를 작성하다니... 밀린 일이 마무리되고 회고를 작성하려고 하다 보니 끝도 없이 미뤄져서 더 이상은 안되겠다 싶어 늦었지만 이제서야 작성하게 되네요. 어디 한번 2022년은 잘 보냈는지 회고해 보고 앞으로의 2023을 디자인해 볼까요?</p><blockquote><p>아래에서부터는 <code>문어체</code> 로 작성해 볼게요!</p></blockquote><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="tldr">TL;DR<a class="hash-link" href="#tldr" title="제목으로 바로 가기">​</a></h2><ol><li><code>정보처리기사</code> 를 취득했다.</li><li><code>코로나</code> 에 걸렸다.</li><li><code>vim</code> 을 사용한다. 그러면서 <code>dotfile</code> 으로 설정 파일을 관리하기 시작했다.</li><li><code>컴포넌트 라이브러리</code> 를 사용하면서 배웠다.</li><li><code>E2E 테스트 코드</code> 를 작성할 수 있는 경험을 얻었다.</li><li>나는 어떤 <code>포지션</code> 의 엔지니어인지 고민하게 되었다.</li><li><code>블로그</code> 를 개편했다.</li><li><code>제주도 여행</code> 을 통해 새로운 견해를 가지게 되었다.</li><li>꾸준한 <code>헬스</code> 를 시작했다.</li><li><code>겸손</code> 해졌다.</li><li><code>Notion 구조</code> 를 개선했다.</li><li><code>산업기능요원</code> 이 되었다.</li></ol><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="dashboard-돌아보기">Dashboard 돌아보기<a class="hash-link" href="#dashboard-돌아보기" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="daily-commit">Daily Commit<a class="hash-link" href="#daily-commit" title="제목으로 바로 가기">​</a></h3><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAqklEQVQImTWOW47CMAxFs4A2cfpIk7oQ2g5CoCK+kFjJ7H8nZ5Sg+bB0rnxsXRN0JV52es24pEhSfFAkLvSz0q9v+ucvRuYFe8vIfUWeG91rx+eMPDbccUH0B9EXZjpdWbYbQzrjp4W2izR+wg4JF2daGWmsx+h6Zz8+jPlAwpm2ShEJRQo1l0Nju4iMJ9ygNDLhulR72jlRdlWUgPnCWEPh/5GhfPxyEf8AGHtMPpPT3vgAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1840" height="1104"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.0f7bf8a.1840.png" srcset="/assets/ideal-img/1.0f7bf8a.1840.png 1840w" width="1840" height="1104"></noscript></div></div><p>꾸준하게 코딩을 한 것이 보인다. 2022년에는 소소하게 오픈소스 기여를 하면서 보낸 거 같다.</p><p>그중에서 <a href="https://github.com/trpc/trpc/pull/3147" target="_blank" rel="noopener noreferrer">https://github.com/trpc/trpc/pull/3147</a> 기여가 설레고 좋았는데 <code>tRPC</code> 와 <code>iron-session</code> 을 함께 사용해야 했는데 예제도 없고 시도한 사람이 보이지 않아서 <code>tRPC</code> 를 이해하고 직접 예제를 만들어서 샘플로 공개했기 때문이다.</p><blockquote><p>제가 만든 예제는 <a href="https://github.com/parkgang/trpc-iron-session" target="_blank" rel="noopener noreferrer">https://github.com/parkgang/trpc-iron-session</a> 에서 볼 수 있어요.</p></blockquote><p>지금까지 대부분 다른 사람들이 기존에 만들어놓은 샘플을 사용했었는데 내가 처음으로 샘플을 만들어서 공개했다는 것에 큰 의미가 있었고 <code>tRPC</code> 를 이해하고 나온 샘플이기 때문에 뿌듯했다.</p><p>커밋의 횟수와 지속성보다는 커밋으로 어떤 이펙트를 냈는가가 더 중요하다고 생각하지만 그럼에도 1일 1커밋으로 나의 의지가 지속된다는 것을 나타내는 것도 의미가 있다고 생각하기 때문에 앞으로 그래왔듯이 계속 진행할 것이다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="board">Board<a class="hash-link" href="#board" title="제목으로 바로 가기">​</a></h3><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAfklEQVQImTWNSQ7DMAwD/Yt6X+QqKWDH8KH5/9NYSEUOPHE4NK01hBDgvUetVZNqxpwTfDCod6T6himlIMYIay2YGV0KqlhrKfjvXjBiEFiMD1ioqfFznkgp6aORa4EFJCLkUhS87y/23hhjKGyklDjn8IwyEa5rYYwJ5kMlP3FCRZQVaWEUAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3862" height="2102"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.375c644.3862.png" srcset="/assets/ideal-img/2.375c644.3862.png 3862w" width="3862" height="2102"></noscript></div></div><p>나는 <code>Notion</code> 을 <code>WiKi</code> 이상으로 방대하게 사용하고 있다.</p><p>이전에는 공부한 내용을 <code>Context</code> 라는 <code>DB</code> 로 한곳에서 관리했는데 이번에 <code>item</code> 타입에 맞게 분리하면서 위와 같이 <code>Project</code> 단위로 볼 수 있게 개편하였다.</p><p>통계적으로는 위와 같다는 것이고 더 자세한 이야기는 아래에서 말해보도록 하겠다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="til">TIL<a class="hash-link" href="#til" title="제목으로 바로 가기">​</a></h3><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAjklEQVQImR2OSwrCQBAFc4vMpzs9vxg1KKiguBA33v9KJTOL2jyK4k21NlR14KMSohBCGKhEgvdIPTMFFVJKA+c8okothUWV3/NNy4nZOSbNC2tb2bZtVM2M/bhTLPO9PxARXBejKTlnWmtjFIlcTzdqqnyeL4oVZjf3og2py/1XL14OO7YYp7ax9KL3/AEo+kTk5j3grQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3756" height="1868"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.ee89a73.3756.png" srcset="/assets/ideal-img/3.ee89a73.3756.png 3756w" width="3756" height="1868"></noscript></div></div><p>여전히 새롭게 배운 것들이 참 많다.</p><p>성장 곡선의 추세로서 <code>TIL</code> 데이터를 사용할 수 있는데 아직도 계속 늘어나는 것을 보아 멀었구나.</p><p><code>TIL</code> 가 줄어들어야 생산 모드로 전환되어 프로젝트를 펑펑 진행할 수 있을 텐데 기술은 새롭게 변하고 나오기 때문에 그런 상황은 오지 않겠지?</p><p>겸손하게 배우며 열심히 해보자 하루하루 공부하느라 나에게 수고했다고 말해주고 싶다!</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="정보처리기사-자격증-취득">정보처리기사 자격증 취득<a class="hash-link" href="#정보처리기사-자격증-취득" title="제목으로 바로 가기">​</a></h2><p>이전 회고에서 산업기능요원이 되고 싶었는데 안되었던 사연을 소개했었다.</p><p>SW 엔지니어로 지내기 위해서 정보처리기사 자격증이 필수적이지 않다고 생각했기에 취득할 생각이 없었지만 산업기능요원이 되기 위해 필수적으로 따야 하는 상황이 발생했다.</p><p>그것도 단 한 번의 기회로 한 번에 취득해야 하는 미션이었는데 이거 부담감이 장난이 아니었다...</p><p>시험도 순수하게 CS를 잘 안다고 해서 잘 풀 수 있는 형태가 아닌 지엽적인 문제가 많이 나와서 시험을 보는 그 순간까지 긴장을 놓지 못했다.</p><p>그래도 공부하면서 몰랐던 새로운 내용도 배우고 대학에서 배웠던 내용을 복습해서 뜻 기쁜 시간이었다.</p><p>부담감을 이겨내고 한 번에 취득했을 때 그 순간은... 크 짜릿했다.</p><p>2022년 상반기는 대학 졸업 후 하고 싶은 공부들을 미뤄놓은 체 자격증 취득만 보고 달려왔기에 이제 내가 하고 싶은 것을 할 수 있나? 라는 설렘도 가득했었다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="코로나에-걸렸다">코로나에 걸렸다<a class="hash-link" href="#코로나에-걸렸다" title="제목으로 바로 가기">​</a></h2><p>이때 대유행이라서 다들 걸렸을 것이라고 생각한다.</p><p>나도 걸리고 대부분의 사람들과 비슷한 증세로 목이 아프고 입맛이 없는 정도고 그쳤다.</p><p>사실 나는 코로나의 아픔보다 더 큰 힘듦이 있었는데 <code>정보처리기사</code> 자격증을 한 번에 취득해야 하는 상황인데 코로나에 걸리면 시험장에 들어갈 수 없어서 시험기간이 가까워졌을 때 걸리면 정말 큰일이었다.</p><p>그래서 위생을 엄청 챙기면서 지내게 되어서 덕분에 위생을 생각하는 좋은 습관을 가지게 되었다.</p><blockquote><p>군대(산업기능요원)이 나에게 참 많은 것을 바꿔구나를 느낀다.</p></blockquote><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="나도-이제-vim-user-">나도 이제 Vim User 😎<a class="hash-link" href="#나도-이제-vim-user-" title="제목으로 바로 가기">​</a></h2><p>SW 엔지니어로 살아가기로 한 거 <code>CLI</code> 와 친해질 수 봤게 없다고 생각했다.</p><p>나는 <code>CLI</code> 를 좋아하는 편이기도 하고 자동화 도구 및 환경을 조성도 사실 <code>CLI</code> 를 기반해서 만들어지는 것이기 때문에 피하는 것이 더 손해라고 생각한다.</p><p>아쉬운 점은 <code>CLI</code> 으로 텍스트 편집은 능숙하지 않았다는 것인데 예시로 <code>k8s</code> 클러스터에서 <code>yaml</code> 을 바꿔보려고 할 때도 <code>vim</code> 사용법을 몰라서 불편했었다.</p><p>물론 굳이 클러스터로 들어가지 않고 <code>GUI</code> 으로 수정하는 방법 등 여러 방법이 있지만 <code>vim</code> 으로 수정하는 것이 더 간편하다고 생각했다.</p><p>이를 타파하기 위해서 <code>vim</code> 을 배워보자 시도하게 되었지만 사실 <code>vim</code> 이 멋져 보이기도 하고 얼마나 빠르게 타이핑이 가능할지 궁금해서 시작하긴 했다.</p><p>어느 적응이 되고 느낀 점은 확실히 빠른 부분은 빠른데 모든 부분이 <code>vim</code> 이 빠른지는 않았다.</p><p>오히려 꼭 <code>vim</code> 을 사용해서 해야 한다는 마인드로 접근하니까 더 느려지는 경우가 있어서 상황에 맞게 잘 쓰는 것도 중요하다고 느꼈다.</p><p>이외 <code>vim</code> <code>config</code> 들의 형상관리 필요성이 느껴지면서 자연스럽게 <a href="https://github.com/parkgang/dotfiles" target="_blank" rel="noopener noreferrer">dotfiles</a> repo를 만들어서 관리하게 되었다.</p><blockquote><p><code>vim</code> 배우니까 이제 <code>해피 해킹</code> 키보드가 사고 싶어졌다.</p></blockquote><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="컴포넌트-라이브러리의-배울점">컴포넌트 라이브러리의 배울점<a class="hash-link" href="#컴포넌트-라이브러리의-배울점" title="제목으로 바로 가기">​</a></h2><p>회사 프로젝트 진행하면서 React Component가 필요해서 직접 만들려고 했는데 아직 제품 형태의 컴포넌트를 만드는 것은 경험이 부족하여 시기 상조라고 생각했고 우선 필드에서 유명한 컴포넌트를 사용해서 잘 추상화된 컴포넌트의 종류와 interface를 경험한 뒤 생각해 보기로 하였다.</p><p>필자는 <code>MUI</code> 를 사용했는데 컴포넌트의 interface 노출에 대해서도 많은 것을 느꼈지만 방대한 유틸리티를 제공하는 <code>MUI System</code> 이라는 디자인 시스템에 대해서 놀랬다.</p><p>컴포넌트들 간의 <code>zIndex</code> 가 충돌되지 않도록 관리, 반응형 레이아웃을 위한 글로벌한 <code>break point</code> 관리, 확장 가능한 스타일링, etc. 정말 많은 기능이 있다는 것을 느꼈고 시맨틱처리, 접근성 등 부가적인 기능들도 많이 제공해서 컴포넌트 라이브러리 제대로 숙달하고 넘어가는 것이 좋겠다는 생각을 할 수 있었다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="e2e-testing-code-작성">E2E Testing Code 작성<a class="hash-link" href="#e2e-testing-code-작성" title="제목으로 바로 가기">​</a></h2><p>회사 프로젝트로 드디어 E2E Code를 작성해 보았다.</p><p>테스트 코드 작성 방법 공부만 해봤지 실제로 실무에서 적용해 본 적이 없어서 궁금했는데 코드를 작성할 때 테스트 러너에서 제공하는 구성요소를 이해하고 잘 만들어야 하더라</p><p>아직 경험이 부족해서 앞으로 지속적으로 작성해 보면서 경험하게 될 거 같다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="엔지니어-포지션에-대한-고민">엔지니어 포지션에 대한 고민<a class="hash-link" href="#엔지니어-포지션에-대한-고민" title="제목으로 바로 가기">​</a></h2><p>이전 회고에서 내가 어떤 포지션의 엔지니어인지 고민이 있었다.</p><p>대중적으로 분류되는 <code>Front-end</code> 와 <code>Back-end</code> 로 분류해야 할 거 같았기 때문이다. 실제로 채용 시에도 어떠한 포지션을 대상으로 채용되니 말이다.</p><p>실무에서 나는 <code>FE</code> , <code>BE</code> 라고 따로 구분 지어서 일하지 않고 관리하고 있는 제품(서비스)를 담당하는 엔지니어로 일하고 있다.</p><p>그러면서 느낀 경험으로는 SW 엔지니어로써 제품을 만들고 유지 보수한다는 것은 <code>FE</code> , <code>BE</code> 의 포지션에 맞게 단편적으로 알고 대응할 수 없다는 것이다.</p><p>이전에는 <code>FE</code> 엔지니어라고 소개하면 이 사람은 <code>React</code> 등을 통해 화면을 그리는 것을 중점으로 알겠구나, <code>API</code> 설계나 <code>DB</code> 설계는 모르겠네라고 생각했는데 글쎄, 실제 앱을 만들어보면 <code>FE</code> 의 단편적인 지식만 가지고는 앱을 만들 수 없으며 화면만을 담당한다고 해도 <code>FE</code> 으로 모든 것을 커버할 수는 없었다.</p><p>엔지니어 포지션에 대한 고민은 나의 얇은 생각으로부터 시작했다고 생각한다. 굳이 지금 포지션을 정해야 한다면 <code>Web</code> 분야 한정으로 <code>Full-stack</code> 이라고 말할 수 있을 거 같다.</p><p>포지션에 대한 고민도 중요할 수 있지만 문제 해결 능력과 제품 개발을 중점으로 하다 보면 어느 정도 가닥이 잡히지 않을까 생각한다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="블로그를-새롭게-개발하다">블로그를 새롭게 개발하다<a class="hash-link" href="#블로그를-새롭게-개발하다" title="제목으로 바로 가기">​</a></h2><p>나만의 블로그가 없어서 만들었다.</p><p>블로그에 추가 기능을 붙이고 싶은데 직접 만든 것이 아니다 보니 확장성이 어려워서 나 개발자인데 하나 만들어보자 하고 만들었다.</p><blockquote><p>이외로 링크드인, 포트폴리오와 같은 Public한 자료에 대해서는 정리가 되었는데 개발 블로그는 내가 온전히 소유한 느낌이 들지 않아서 찜찜하기도 했다.</p></blockquote><p>만들면서 내 스타일에 맞게 카테고리 분류 및 기능을 추가하였는데 결과물을 보니 아이덴티티도 있고 뿌듯하다.</p><p>더 자세한 블로그 개발 건은 따로 글을 출간하려고 한다.</p><blockquote><p>사실 이미 어떻게 만들고 뭘 배웠는가에 대한 글 초안은 있는데 아직 글로 Publish 하지는 못했다ㅠㅠ</p></blockquote><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="개인힐링-제주도-여행">개인힐링 제주도 여행<a class="hash-link" href="#개인힐링-제주도-여행" title="제목으로 바로 가기">​</a></h2><p><code>정보처리기사</code> 도 취득하고 <code>블로그 개발</code> 도 했겠다 나에게 보상으로 여행을 떠나고 싶었다.</p><p>사실 이전부터 <code>산업기능요원</code> 되면 가려고 아껴둔 <code>제주도 여행</code> 이었는데 아쉽게도 여행 가는 시점에 <code>산업기능요원</code> 으로 편입되지는 않았다...</p><p>처음으로 혼자 떠나본 여행인데 처음 겪어보는 자유로움을 느낀 여행이었다.</p><p>이전까지는 코딩을 가장 큰 우선순위를 가지고 살아왔던 거 같은데 이번 여행을 기점으로 나의 마인드에 많은 변화가 생겼다.</p><p>꼭, 코딩 시간을 많이 투자하고 잘한다고 해서 행복이 아니라는 것을 느낄 수 있었다. 당연한 거라도 생각할 수도 있지만? 이런 것도 실제 경험을 해봐야지 알겠더라</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="헬스웨이트-트레이닝을-시작하다">헬스(웨이트 트레이닝)을 시작하다<a class="hash-link" href="#헬스웨이트-트레이닝을-시작하다" title="제목으로 바로 가기">​</a></h2><p>원래 이전부터 운동을 하려고 여러 번 시도했으나 조금 하다 보면 지금 하고 있는 방법이 맞나? 싶은 생각이 들면서 의지도 줄어들고 야근으로 몇 번 헬스장을 못 가게 되며 포기하게 되었다.</p><p>제주도 여행 기점으로 다시 도전을 하게 되었고 회사 동료 중 헬스를 잘 아는 사람이 있어 도움을 받으면서 진행할 수 있었다.</p><p>덕분에 하루가 너무 알차고 항상 에너지가 넘치니 너무 좋았다. 진작해야 됐었다 여러분 다들 헬스하세요!</p><p>자전거, 웨이트, 등산이 나의 운동 취미로 잡을 수도 있다고 생각이 들었다.</p><p>이렇게 운동 습관 잡은 것만 해도 한 해에 이룰 수 있는 큰 목표라고 생각한다. 다른 사람들은 다이어트하기 운동하기를 한 해 목표로 잡는데 그 목표를 이뤘으니 말이다.</p><p>번외로 개발자로 살아가면 건강 문제 생기는 것을 많이 보아서 미리 건강 관리하는 것이 좋다고 생각한다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="겸손해지다">겸손해지다<a class="hash-link" href="#겸손해지다" title="제목으로 바로 가기">​</a></h2><p>기본 <code>CRUD</code> 앱이 나는 쉽다고 생각했다.</p><p><code>FE</code> 도 해보았고, <code>UI</code> 도 관심이 많아 설계에 큰 어색함도 없었고, <code>RDBMS</code> 기반으로 스키마를 설계해서 <code>API</code> 도 만들어 보았고, 그러한 앱을 배포도 해보았기 때문이다.</p><p>근데 실전으로 프로덕트 퀄리티로 만들 때는 달랐다.</p><p>코딩이 어려운 것이 아니라 협업하면서 interface를 맞추고, 사용자에게 가치를 전달할 수 있도록 기능 단위를 잘 나눠서 만드는 등 많은 노력이 필요한데 실제 경험하기 이전에도 왜 해야 하는지 알고 있었지만 실제로 하려고 하니까 어려운 부분도 많았고 분명 알고 있고 쉽다고 생각했는데 Output이 합리적이지 않은 속도라서 고통의 연속이었다.</p><p>이런 경험을 많이 하면서 겸손해지었고 이런 성장통을 잘 겪어서 더 나은 개발자가 될 수 있다는 기대를 가지게 되었다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="notion의-구조를-개선하였다">Notion의 구조를 개선하였다<a class="hash-link" href="#notion의-구조를-개선하였다" title="제목으로 바로 가기">​</a></h2><p><code>Notion</code> 을 <code>WiKi</code> 이상으로 하드하게 사용하고 있는데 정리를 해도 계속 분류가 필요한 데이터가 필요하려 <code>DB</code> 를 새롭게 만들어 분류하였다.</p><p>아래와 같은 형태로 구분하였는데 어째 또 바꾸게 될 거 같다는 생각이 들지만 이제 정착해서 사용하고 싶다.</p><div style="width:150px;margin:auto"><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAmCAYAAAD9XArwAAAACXBIWXMAABYlAAAWJQFJUiTwAAAChklEQVQ4jXWUW2/TQBCF+zfaxLvrje3Y6736koTS0pYWtaqAIoToE6AKIZ555c8fNBtw7bo8bGTHn+ZyZs4eCCHQnp5js9uhCQFN06DrOkgpwRgDfeec44B+amPRdh2MMWjbNoJ1XSNN0wgRfCAERypEhOgjgXTofQLSw2q1gnMOWushIv03Sz0GCaJTVdUATiJaaweQnouiGNLOQIoaQsDR0dEA8f/VSBG99xHgIziCMpVD6s1mE0FSYBYxWxcRVEphu91G0an7CbiSEunn99AnJ9j1PVIpcXh4iOVyOYAxNeMCSuU49hmcD7EZip7n+bTrhAvUWYrXZgXrfExL8AxkQqCSEhfrHLXW2G62WK/XSJJkWiPjDOtM4aW9hg8eZVlORiceBWfIshLOnEXQaBN1HUOP27PKIXQXayMd+76fzFnsUwvkK44XdgFrXRR6LI14jCjgjIbVJbwPcTpjgD8ubopN6+D7bZwGTefpQojYTLKEVAb27h7OmjhCOuSZCUj1dCHg18MDVK2QZdksrdiDCRrv8OP7N5RVhaosJ17h/2pcJhytLvDxwxnK2qDv926cycOTBQq7xeWX3wjOQGszGR8f9pElyEqDq9s7BG8RmmbmFzEsbpbj3bmF0Wq4CGbgMmFoTImfny5Qqf0FQNLMtoczhrxYoz8+h3cugrSTs30UnEFpj9LvELyPacmRM1AKjtPuFm3YwHmHxWIR0850ZExAFRLXp3lsZHyV8IkVGEelcty8aWJKssFTsffmYgJGSdzf5NFcT2sbgRxaFXh7FdC2Hfqum0xktOEcZZHh1c7EDSdpKP0M3EfMcHOpUWuD8NeJz4ACXqf4epug68hY3bMR/wAQ1SnJ8tK2ZwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="396" height="1518"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.4a94d8a.396.png" srcset="/assets/ideal-img/4.4a94d8a.396.png 396w" width="396" height="1518"></noscript></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="산업기능요원-병역특례-이-되었다">산업기능요원 (병역특례) 이 되었다<a class="hash-link" href="#산업기능요원-병역특례-이-되었다" title="제목으로 바로 가기">​</a></h2><p>드디어 산업기능요원이 되어서 병역 특례를 받을 수 있었습니다! 🥳</p><p>받은 시점은 <code>2023</code> 이라서 사실 2023 회고에 등장해야 하지만 Context상 <code>2022</code> 이 더 맞기에 해당 회고에 등장시켰습니다.</p><p>받는 동안 정말 많은 어려움이 있었습니다. 처음에는 안된다고 하여 포기를 하고 살아갔지만 도중에 될 수 있는 기회가 생겨 기대했지만 계속되는 문제 발생으로 편입되지 못했습니다.</p><p>군대를 갈지 안 갈지 모르는 상황이다 보니 이게 삶을 살아가는데 집중이 어렵더군요. 그래도 받을 수 있어서 다행으로 생각합니다.</p><p>좋은 기회로 생각하고 열심히 해보려고 합니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="앞으로는">앞으로는?<a class="hash-link" href="#앞으로는" title="제목으로 바로 가기">​</a></h2><p>블로그에 올리고 싶은 글이 많은데 회사일에 집중하고 <code>Backlog</code> 가 밀리다 보니 글을 쓸 수 있는 시간 확보가 쉽지 않네요.</p><p>이전 회고에서 가장 걱정하던 병역 문제가 해결되어서 한시름을 놓고 제 커리어에 대해서 집중할 수 있게 되었습니다.</p><p>오히려 전에 비해 고민이 더 늘어난 거 같습니다. 막연하게 시간을 보내는 것이 아니라 어떻게 성장을 위해서 방향을 잡아야 할지 고민이 많아진 거 같아요.</p><p>앞으로 저에게 필요한 것은 새로운 공부보다 배운 것을 숙련하여 성공 경험을 만드는 것이라고 생각이 되네요.</p><p>다음에는 또 얼마나 성장했을지 기대하며 이만 글을 마무리하도록 하겠습니다.</p><p>읽어주셔서 감사합니다 :)</p>]]></content>
        <category label="개인 회고" term="개인 회고"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Docusaurus으로 블로그 개발 및 마이그레이션 후기]]></title>
        <id>/2022/12/03/blog-migrating-to-docusaurus</id>
        <link href="https://parkgang.github.io/blog/2022/12/03/blog-migrating-to-docusaurus"/>
        <updated>2022-12-03T11:00:00.000Z</updated>
        <summary type="html"><![CDATA[기존에 gatsby-starter-bee 으로 블로그를 운영하면서 아쉬움이 발생했다. 새로운 기회를 찾아 docusaurus 으로 블로그 개발을 마음잡았고 어떻게 디자인하고 바뀌었는지 후기를 공유합니다.]]></summary>
        <content type="html"><![CDATA[<p>기존에 <a href="https://github.com/JaeYeopHan/gatsby-starter-bee" target="_blank" rel="noopener noreferrer">gatsby-starter-bee</a> 으로 블로그를 운영하면서 아쉬움이 발생했다. 새로운 기회를 찾아 <a href="https://github.com/facebook/docusaurus" target="_blank" rel="noopener noreferrer">docusaurus</a> 으로 블로그 개발을 마음잡았고 어떻게 디자인하고 바뀌었는지 후기를 공유합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA+0lEQVQImRXGS0rDQBgA4HHVZh7OTNKZtDZt03TKYAsuRIgM2EV9NGSjJ/AMHsEDuHAp4g10YUEEL1GyVihk3wu0JPzSxQcfsu7qxV7kG+Oy9dgtSpPOS5NelmOXlcP0ep2c32yms/wNESqXlEkgzIcm5rD/XsPj4BEJhAhQqvONiNAf3d4IEjPZRb1hTUVYY6Hrs4mt40G887iC8GiwRJj579H0FOws2xq3qLqxrShX1evjQ3V/d7s9aHDQ7egTURZ8+fYEVDqHVppDlBzDoQwhGRno9y1QoUG2Oj+ICvVEqP9HMF8RzAqP+gUVqmiyoMA8WDGpf0XQfv4HRsJLHS+9boIAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3186" height="2074"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/thumbnail.45c21ea.3186.png" srcset="/assets/ideal-img/thumbnail.45c21ea.3186.png 3186w" width="3186" height="2074"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="새로운-블로그-개발-계기">새로운 블로그 개발 계기<a class="hash-link" href="#새로운-블로그-개발-계기" title="제목으로 바로 가기">​</a></h2><p>저는 평소에 <code>Notion</code> 을 통해 늘 정리를 해오는데 그중 공유할 만한 주제가 보이면 블로그로 내용을 공유하는 편입니다.</p><p><code>링크드인</code> , <code>포트폴리오</code> 와 같은 <code>Publie</code> 한 자료에 대해서는 얼추 정리가 되어서 <code>선순환</code> 을 위한 구조가 만들어졌다고 생각이 되는데 <code>개발 블로그</code> 는 아직 정리되지 않은 느낌이 들었습니다.</p><p>개인적으로 아쉽다고 느껴지는 기능들이 있었고 이를 개선하고 싶은데 덕지덕지 커스텀 하는 것이 아닌 정형화된 <code>프레임워크(framework)</code> 에서 기능을 붙어나가고 싶다는 니즈가 있었습니다.</p><p>아니면 아쉬운 기능을 내가 개발하는 것이 아닌 이미 잘 반들어진 <code>블로그 서비스</code> 를 사용하면 어떨까? 생각도 해보았습니다.</p><p>하지만, <code>블로그 서비스</code> 를 사용하면 운영 비용은 감소할 수 있겠지만 <code>코드 하이라이팅</code> 같은 것들이 부족할 것으로 생각되었고 앞으로 계속 운영할 생각이 있다면 내가 직접 제품을 만들고 운영해 나가는 것이 더 많은 경험을 얻을 수 있다고 생각했습니다.</p><p><code>블로그</code> 라는 것이 매우 특별한 요구사항을 가진 <code>제품</code> 은 아니기에 이미 <code>SEO</code> 최적화 및 <code>블로그</code> 를 위한 기본적인 기능이 포함된 <code>도구</code> 들이 많이 있을 것이라고 생각하고 새롭게 블로그를 개발하자라는 원대한 목표를 잡게 되었습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="어떻게-개발할까-도구-선택">어떻게 개발할까? (도구 선택)<a class="hash-link" href="#어떻게-개발할까-도구-선택" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="요구-사항">요구 사항<a class="hash-link" href="#요구-사항" title="제목으로 바로 가기">​</a></h3><p>기본적으로 기존에 <a href="https://github.com/JaeYeopHan/gatsby-starter-bee" target="_blank" rel="noopener noreferrer">gatsby-starter-bee</a> 에서 운영하던 단점을 보강해줄 수 있는 <code>환경</code> 으로 선택하고 싶었습니다.</p><p>개인적인 아쉬움은 아래와 같았습니다.</p><ul><li><code>Header</code> 에 따른 목차 기능이 없음 (TOC)</li><li><code>검색</code> 기능이 없음</li><li>여러 개의 글이 연결된 <code>시리즈</code> 별 분류를 나타내기 어려움</li></ul><p>아래의 목적에도 부합하기를 원했습니다.</p><ul><li><code>React</code> 으로 개발</li><li><code>유지보수</code> 가 활발한 프로젝트</li><li><code>커스텀</code> 에 대해서 열려있음</li><li><code>마크다운</code> 으로 글을 작성할 수 있어야 함</li></ul><p>그리고 <code>CI</code> 에서 주기적으로 가공되어야 하는 <code>Blog</code> 특성상 <code>yarn berry</code> 를 사용하면 얼마나 빨라질 수 있는지 궁금해서 <code>yarn berry</code> 도 사용하고 싶었습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="docusaurus-로-결정"><code>Docusaurus</code> 로 결정!<a class="hash-link" href="#docusaurus-로-결정" title="제목으로 바로 가기">​</a></h3><p>찾던 중 <code>Docusaurus</code> 라는 것을 발견했는데 마음에 쏙 들더군요.</p><p>문서 작성을 위해 <code>추상화</code> 도 잘 되어있고 기본적으로 탑재된 <code>UI/UX</code> 가 만족스러워서 적은 <code>커스텀</code> 으로 좋은 <code>블로그</code> 를 만들 수 있겠다는 생각이 들었습니다.</p><p>아쉬운 점은 <code>framework</code> 이다 보니 나만의 <code>개성</code> 은 떨어지겠지만 기본적인 <code>퀄리티</code> 가 좋아서 나만의 <code>아이덴티티</code> 를 살릴 수 있도록 <code>커스텀</code> 하자라는 생각을 했습니다.</p><p><code>Docusaurus</code> 의 가장 큰 결정은 태생이 <strong>오픈 소스 프로젝트 웹 사이트를 쉽게 구축, 배포 및 유지 관리하기 위한 프로젝트</strong> 이기에 <code>개발 블로그</code> 로 운영하기 위한 <code>코드 하이라이팅</code> 기능도 매우 강력하고, <code>문서화</code> 도 잘 되어있고, <code>커스텀</code> 을 위한 확장성도 잘 오픈 되어 있다는 것입니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="docusaurus-장점"><code>Docusaurus</code> 장점<a class="hash-link" href="#docusaurus-장점" title="제목으로 바로 가기">​</a></h3><p>당시 너무 신나서 <code>장점</code> 을 쭉 정리했는데 아래와 같습니다.</p><table><thead><tr><th align="left">내용</th><th align="center">비고</th></tr></thead><tbody><tr><td align="left">기본적인 레이아웃 제공</td><td align="center"><div class="imgContainer_g3cu spacer_xInT" style="width:200px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA60lEQVQImS3KPU7DMBgAUB8CYsf2578kbonAbVe6QCuhNNASJATq0hMwcJhSCan3QAwgJhhKRjhL1Sj6EIjhbY9U89uH+WLxXU6rzag4r8dFWY+LSX12Ma0ns8vN1fXNVzmrHkkejt4O8hxHJ6c4PB6i73Qx+xdCDwe9PnZ9551wCc9CSEwyvzsMoTXGtqB0KyS0WpsdKI00oq/EuORJG4smTbehP2isSxoW8yairJEStgoUci5eiHXphwSF1jnMvEetNUoJCAAYc4GUxb/xkxib3ill10LKpbZmBQpWQog/lLHl3n60jii7/wGs1k+B4sfvvAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3808" height="2246"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.7e408fd.3808.png" srcset="/assets/ideal-img/1.7e408fd.3808.png 3808w" width="3808" height="2246"></noscript></div></div></td></tr><tr><td align="left"><code>오픈 소스</code> 라 <code>커뮤니티</code> 가 있고 <code>유지 보수</code> 가 됨</td><td align="center"></td></tr><tr><td align="left"><code>검색 기능</code> 을 붙일 수 있도록 고려 및 문서화되어 있음</td><td align="center"></td></tr><tr><td align="left"><a href="https://docusaurus.io/ko/docs/sidebar/items" target="_blank" rel="noopener noreferrer"><code>docs</code> 의 경우<code>사이드 바</code> 로 <code>아이템</code> 표시</a> 가능</td><td align="center"></td></tr><tr><td align="left"><code>SEO</code> 관련 기본적으로 고려됨</td><td align="center"></td></tr><tr><td align="left">강력한 <code>코드 하이라이팅</code>,<br><code>파일 이름</code> 표시 가능,<br>내가 어떤 코드를 설명하고 있는지 <code>라인 하이라이팅</code>,<br><code>코드 복사 기능</code></td><td align="center"><div class="imgContainer_g3cu spacer_xInT" style="width:200px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAdklEQVQImWXF0Q6CIBiAUZ4ivAhrpsYPykIE7vL9n+prtrZyXZwdtaSVNRdKrTxi5GwMumnQ+uukG5SXiTBHhtFjLjfaa//etL86VM1PatkYbcK6jOx8OQg+o+wwMbsF1wekDwyd+zN2ghKXsLIiH3ebDqzsR17bJUlokEsBrAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1950" height="994"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.53c8f68.1950.png" srcset="/assets/ideal-img/2.53c8f68.1950.png 1950w" width="1950" height="994"></noscript></div></div></td></tr><tr><td align="left">강조 기능, 이것도 정말 필요했던 기능입니다.<br>이런 것이 없어서 죄다 <code>인용구</code> 로 넣어서 처리하고 있었거든요... <br><div class="imgContainer_g3cu spacer_xInT" style="width:200px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAANCAYAAACQN/8FAAAACXBIWXMAAAsTAAALEwEAmpwYAAABv0lEQVQokW2PbU9SAQCF79faymVlTRDCIi91gdQBF9B7EUJrbW1a8SK2ZDHM1Vrvw5QsbCPemR9cm6EijT7lXD/BfkT+D4PFp6dB+aGts52d7dnZzo6geCa4fmMS/8RNJIsdyWL7j+0Io7KC7FBwti0ruJwqTlntMNkxisvlwTEkI8zF5onPPyI8EyUcmSUUmf2b95hus5koz5+8QCjlS7xLpfB6vKiqF+eIB7cyhntExTPmw+VWWC2VEYq5Ir9aLZqNBmtrH+nV6JAkKwMmCdEkoek7x1LiNUIhW+BQG5tbdJ04haHfiE5v6OTpnt4/xfb0oSqVDbpP9jAgXkY0mTFeNHHmrJbkwuK/xfX1CkeOHqdPZ0CrM6DR6jnW1U3i5SuEcqHEwc8Dms0GtdpnbHYZ39VxfD4//vFrKKqX1UIRIRaNU9/+QiadI5POUq/W2fpUpZgvk/uQ5+3yCk8fP0NILCS5H4sTDEaYnAoQCt0lEIwwdStIIDjN7TthkssphM1qjZ1vu3zf22PuwUP050XM1mEuSVbMlkEuGEVW3qcRvu7ssv9jn1arRSab65wYHLJhNluxXhmm3yiyuPSG38YaGVJ/b0NWAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1230" height="1610"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.be667db.1230.png" srcset="/assets/ideal-img/5.be667db.1230.png 1230w" width="1230" height="1610"></noscript></div></div></td><td align="center"><div class="imgContainer_g3cu spacer_xInT" style="width:200px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAbklEQVQImT3FywqCQABA0fmExAhnjNQc52GjYUpSC0GKHqv+/2tuVNDicIQPLc3+QGg78q1Bqg0yzZDpb/WxzhBaG0zlsMbj3I7aB5yt/1faUuQl4jUVPM+K23HF4yS5jwmXPuY6LL/mLmJqFrwBPnosGxBh/8YAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1964" height="670"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.f67c7f6.1964.png" srcset="/assets/ideal-img/3.f67c7f6.1964.png 1964w" width="1964" height="670"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT" style="width:200px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAOElEQVQImWOQNRf9L2ks+F/UgPe/oA4nViykzfWfwV1d+b+LisJ/aymx/5YSwv8tJUQwsLmEyH8AMv8bNBGdQFcAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1950" height="434"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.31bfedb.1950.png" srcset="/assets/ideal-img/4.31bfedb.1950.png 1950w" width="1950" height="434"></noscript></div></div></td></tr><tr><td align="left"><code>Header</code> Link 기능</td><td align="center"><div class="imgContainer_g3cu spacer_xInT" style="width:200px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAVElEQVQImQXBSw5AMABAQZeRWEhQ1WCjpfWLYEFZ4P63eGaCsqrRpsW0HZnIkYUizQRSKXRjkCIhikKCzjqe9+M4Peu246+bYZyw/cB1eubFkYqYH2onIzuqmEGHAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="534" height="108"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.44d7f06.534.png" srcset="/assets/ideal-img/6.44d7f06.534.png 534w" width="534" height="108"></noscript></div></div></td></tr><tr><td align="left">위로 바로 올라가기 버튼</td><td align="center"><div class="imgContainer_g3cu spacer_xInT" style="width:200px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAALCAYAAABGbhwYAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAtklEQVQYlW2Q2wqCQBRF/YnmYtYHVBSFUOCD0UQqXiAKgujBl/7/E3bsA04qPqyZ5T7HmeEExoYg2ljZ++heFowD+vjbsLET7jOlfWPnXc2fSFabHZLUCfT+6dLIv9fbPYr6AZfVAp0Za4OrU5fjmjd4vVuBzsxfzcWGc5xdgay849N+BToz1vwbldI4xCcUzROXWyXQmbE2GI8NI8THBC6rBDqzyfEorREtlgJ9cjz/ARthPPAf1m+dyQm5PdYAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="162" height="178"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.d949521.162.png" srcset="/assets/ideal-img/7.d949521.162.png 162w" width="162" height="178"></noscript></div></div></td></tr><tr><td align="left">읽는 시간 유추 표시</td><td align="center"><div class="imgContainer_g3cu spacer_xInT" style="width:200px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAASUlEQVQImR3MSQ6AIBBEUa6iiEoPDAJGvf+9yjSrSn5eyvktoF4NoglnJLQ+wKKIxFhWj1wqiAUu7Af6uKEpz/C831zDBu3A4A+/Th5ATJ82LAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="340" height="62"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.3695b01.340.png" srcset="/assets/ideal-img/8.3695b01.340.png 340w" width="340" height="62"></noscript></div></div></td></tr><tr><td align="left">탭 기능</td><td align="center"><div class="imgContainer_g3cu spacer_xInT" style="width:200px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAgklEQVQImR3OQQ6DIAAAQT5hUUCJ0BoEa1VIe8D4/19tE297mMMKpQxjnLHTC6N7tu1g3zPWjoQQ745xQejBMv0yflsxveU4CvW8cM7foNaTnAvChYBPEZcig3+iWoWSHV2naVtF00ik7BBxTnzWjSW9yeVLzYXLex7K3CvGDGjd8wd4pznHbCoFVQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="512" height="210"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9.0b902ee.512.png" srcset="/assets/ideal-img/9.0b902ee.512.png 512w" width="512" height="210"></noscript></div></div></td></tr><tr><td align="left">라이브 에디터</td><td align="center"><div class="imgContainer_g3cu spacer_xInT" style="width:200px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAALCAYAAABGbhwYAAAACXBIWXMAAAsTAAALEwEAmpwYAAABI0lEQVQYlVWQ2UrDQBhG8yI1mSXNTNJlkiZqmy5R1AuRinij4AOI+DB2dQGf9UjSWvHiXAwcvn843mw6Y1JVFCcleXFKzw3op/kOl5Nmx0yqC7yHxycWyw2L1ZbV9pPl+r1hsX7nbbnh4+ub55dXPGNixsMRw3JEXhQ418OlKT3niOO4od2O8Hw/wEaGxMZ0OhZrI6QU6FAjRMBRq0XteEIojDFYE2GNIQxDgiBoEELskTsxsfWSRWmN7/t/4p7gVzSRQWu9R6GUQirVLB0W6/vtyO4lSVgTKqSUzfrhj3HSw2U5na6j28/ou8GuZdNzgMsK4qSLV0ee395zM7/j8uqaUTmjnJxRTipG44rx9Jw0K/Ck1NQtral7Geq3UuE/hNT8ALG1r6MvGRdKAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1294" height="1378"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/10.4ae4174.1294.png" srcset="/assets/ideal-img/10.4ae4174.1294.png 1294w" width="1294" height="1378"></noscript></div></div></td></tr></tbody></table><p>이외도 너무 많은데 대부분 <a href="https://docusaurus.io/ko/docs/markdown-features" target="_blank" rel="noopener noreferrer">마크다운 기능</a> 문서를 보면 알 수 있습니다.</p><p>더불어 <code>플러그인</code> 기능도 있어 다른 사람이 만들 기능을 손쉽게 붙어서 사용할 수 있습니다.</p><p>이런한 것은 <code>React</code> 등 이용해서 만들 수 있기야 하겠지만 기본적으로 제공하고 수정이 열려있는 점은 큰 장점이라고 느껴졌습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="설계-및-진행">설계 및 진행<a class="hash-link" href="#설계-및-진행" title="제목으로 바로 가기">​</a></h2><p>이전에 운영되고 있던 것을 안전하게 새로운 것으로 안착시키는 것이 제일 중요하기 때문에 <code>마이그레이션</code> 이 문제가 없는지 꼼꼼히 확인이 필요했습니다.</p><p>또한, <code>Docusaurus</code> 라는 공통된 플랫폼을 사용하기 때문에 개성(<code>아이덴티티</code>) 가 있어야 된다고 생각하여 <code>리서치</code> 만 1주일 넘게 진행했습니다.</p><p>만드는 것에 급급하지 않고 블로그에 들어왔을 때 유용한 <code>UX</code> 도출하고 싶었습니다.</p><p>그래서 우선 <code>Docusaurus</code> 의 기능을 이해하기 위해서 <code>공식 문서</code> 도 모두 읽고 <code>PoC</code> 를 꼼꼼히 거쳤습니다.</p><p>개인적으로 정말로 내가 <code>PoC</code> 결과대로 만들어진다는 생각에 이 시간을 제일 재미있게 할 수 있었습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="docusaurus-에서-고려할-것"><code>Docusaurus</code> 에서 고려할 것<a class="hash-link" href="#docusaurus-에서-고려할-것" title="제목으로 바로 가기">​</a></h3><p>종합해 보니 아래의 정도로 추려졌고 관련해서 어떻게 되는지 찾아보고 설계할 수 있었습니다.</p><ul><li><code>SEO</code> 최적화는 어떻게 달성되는가? (e.g. <code>og 썸네일 이미지</code> 및 <code>HTML</code> 에 <code>meta</code> 는 어떻게 생성되는가?)</li><li>댓글 기능은 어떻게 붙어야 하는가?</li><li>검색 기능은 어떻게 붙어야 하는가?</li><li><code>URL</code> 은 어떤 구조로 생성되는가?</li></ul><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마이그레이션-작업-진행">마이그레이션 작업 진행<a class="hash-link" href="#마이그레이션-작업-진행" title="제목으로 바로 가기">​</a></h3><h4 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="gatsby-starter-bee-글-마이그레이션"><a href="https://github.com/JaeYeopHan/gatsby-starter-bee" target="_blank" rel="noopener noreferrer">gatsby-starter-bee</a> 글 마이그레이션<a class="hash-link" href="#gatsby-starter-bee-글-마이그레이션" title="제목으로 바로 가기">​</a></h4><p>사실 이 작업이 제일 걱정되었습니다.</p><p>기존에 잘 등록된 글이 <code>URL</code> 구조가 변하면서, 혹은 <code>HTML</code> 이 다르게 가공되면서 문제가 생기지 않을까 말이죠</p><p>제 개인적으로는 <code>Docusaurus</code> 의 <code>Plugin</code> 인 <a href="https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-client-redirects" target="_blank" rel="noopener noreferrer">plugin-client-redirects</a> 를 이용해서 <code>마이그레이션 이전 URL</code> 을 등록하면 잘 처리되지 않을까 생각했습니다.</p><p>관련해서는 자세하게 <a href="/blog/2022/10/23/same-page-multiple-urls-seo/">동일 페이지, 다중 URL의 SEO 처리 방식</a> 에 글을 작성해놨습니다.</p><p><code>마이그레이션 작업</code> 을 진행하면서 문제를 만날 수 있었는데 이전 <code>블로그</code> 에서는 <code>/using-recoil-in-next.js</code> 와 같이 <code>.js</code> 으로 끝나는 <code>URL</code> 의 글이 있었습니다.</p><p>문제는 <code>Docusaurus</code> 의 <code>Build</code> 가공 물이 <code>{경로}/index.html</code> 으로 만들어져서 경로에 해당하는 디렉터리의 <code>index.html</code> 가 서빙되는 것이 아닌 해당 <code>.js</code> 파일이 없다고 <code>404</code> 가 응답되는 문제가 있었습니다.</p><p>그래서 <code>Build</code> 이후 <code>Shell Script</code> 를 통해서 <code>cp ./using-recoil-in-next.js/index.html using-recoil-in-next.js.html</code> 으로 디렉터리가 아닌 파일로 존재하도록 해서 문제를 해결했습니다.</p><p>이거 때문에 당시 식은땀을 흘린 기억이 나네요.</p><h4 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="velog-글-마이그레이션"><a href="https://velog.io/" target="_blank" rel="noopener noreferrer">velog</a> 글 마이그레이션<a class="hash-link" href="#velog-글-마이그레이션" title="제목으로 바로 가기">​</a></h4><p><a href="https://github.com/JaeYeopHan/gatsby-starter-bee" target="_blank" rel="noopener noreferrer">gatsby-starter-bee</a> 블로그 운영 전에 <a href="https://velog.io/" target="_blank" rel="noopener noreferrer">velog</a> 에서도 글을 작성한 것이 있어 이참에 이것도 옮길까 했는데 <code>velog</code> 에는 <code>TIL</code> 와 같이 <code>일기장</code> 형식으로 작성한 글도 많고 무엇보다 <code>102개</code> 로 글의 양이 꽤나 많아서 고민이 되었습니다.</p><table><thead><tr><th><code>velog</code> 당시 시리즈 물</th><th><code>큐 레이션</code> 을 위해 <code>velog</code> 글 <code>Notion</code> 에 정리</th></tr></thead><tbody><tr><td><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAARCAYAAADkIz3lAAAACXBIWXMAAAsTAAALEwEAmpwYAAACiUlEQVQokUXQz0uTcQDH8W+6uWmbe55n27Pt2bPN0inqdItW2/SZgVNn6nCjdJkp/mg6g85al5CIIB/0AQ9dJIKSKHYSITwLQfQHVHgJIzLbdNlB/LLtEwbR4XX4wPv0Ibtfd8dyR/mtw/zR62zuMJPN5TM/D35l9nP5zN5+9s1+9nDry+63CTI9m16dmJ7EtetDSCaHMXJjBMmR0b9ujt7CbDqN+fmFp+QMIU+MJjNavReOKysrC2q1ulBRoS5UqNUFtUp1rNVq4fN5l4lOp5PrG9zojXZTwS5CEIS/bDYbjEYj5XkekiQphGVZmeM4cAxDWUM1GMYA9nSzHFiWpSaTCW1tbQphGUbmjCYYrQI12ezgRRE8bwbLsdAxzP9QQ4jcEk1g8XORPtoBxte34bQ74XO60GSzULPZjNBpWKUqk92XwxhcWqc9D58jkLoPvUmEleEhaDSUZTm0t7crxMAwMm82w2UXqGDhIdosiI0MI54aQzw1Smvdblzy+xWi1+tl0eFEKBCk3hYPLvr9WNl8hZX3G3jx8QMNdkho9XgUYjAYZIvFghrXeepwnoMoOhDulNARCUHq7KCi04FAIKAQXTmRXaFeRFbfUenBJjwzz2BrGoAYXoTov015q4hQKKiQqjIiN4SjiK9t0y75La7eW4O1sR+W4AIE/zjlbXZ0hMMKqTpbvdRQKyA56KV9Pc2liFRXivV1lwb7u0qx3gitcbkQPg0JqVxKDDShuHf3ZOPlUFF5HCv+Pvhe/LHzqYhi6SQaicDr860QjVa33NrswtxUCP1RDyJXGjGXmkJ6cgJ3ZmZQX1d3evgq4VhDs6pCnySkPE5IeYIQVYIQ8k9co9UOBwKBlj9kNhUXWGwU2QAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="4014"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/11.0de2f8c.2384.png" srcset="/assets/ideal-img/11.0de2f8c.2384.png 2384w" width="2384" height="4014"></noscript></div></div></td><td><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAm0lEQVQImS3NUQ6DIBBFUXehMMDAgBVbSeP+F3cbrb8vJ/dNpRSSKrU0jn0np4iIIEFouRJEcM4x5Zzx3lO0ch4De+C1jXdnrYV5XphqNVqrBC30zxcvgRDCjV/bB9XCstyw0qqhZWOcX7KlGznxnH1nLZX5gmbGhWNUzjF4meH9//rYdjTGfzGlRJCAcwvb+sZ0xYu/q7134gN/UU5STwXeG4EAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="4096" height="2560"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/12.e942794.4096.png" srcset="/assets/ideal-img/12.e942794.4096.png 4096w" width="4096" height="2560"></noscript></div></div></td></tr></tbody></table><p>그래도 의미가 있는 글도 있어서 <code>Notion</code> 으로 글을 모두 뽑아서 <code>큐 레이션</code> 후 필요한 글만 <code>Blog</code> 에 올리는 방향으로 <code>velog</code> 에서 작성된 글도 <code>마이그레이션</code> 을 완료할 수 있었습니다.</p><p><code>velog</code> 에는 <code>통계</code> 기능이 있어서 <code>마이그레이션</code> 하면서 어떤 글이 인기 있는지 알 수 있었습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAcUlEQVQImW2OOQ7DMAwE9QUdIUVKsWy4MPL/B05gOU6aFINZLLdgEBF676jqX8wM90YopTDGwN1ncfmmYe7EGAkpJcpDyTmTc/r4yuft9hx637G2Um1B1L5obYjU31C0Uq1jbcF8mbk9N9b9xdgOzvfeJqc/qROf+toAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3524" height="1676"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/13.28524ba.3524.png" srcset="/assets/ideal-img/13.28524ba.3524.png 3524w" width="3524" height="1676"></noscript></div></div><p>이전에 작성된 글을 보면서 추억에 잠길 수 있었네요. 🥺</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="디자인">디자인<a class="hash-link" href="#디자인" title="제목으로 바로 가기">​</a></h3><p>나만의 <code>아이덴티티</code> 를 위해서 <code>커스텀</code> 이 가능한 영역에서 여러 시도를 많이 해보았다.</p><p>어떠한 경로를 통해 내 블로그에 <code>랜딩</code> 했을 때 이 블로그는 뭔가 다르다, 이런 특징(갬성)이 있구나를 느낄 수 있도록 하면서 글을 읽으면서도 <code>가독성</code> 에 문제가 없도록 어떤 <code>블록(컴포넌트)</code> 를 사용하면 좋을까, 이렇게 했을 때 아쉬운 부분은 없을까?, 글을 작성해 보며 부족한 부분은 잡아내고 <code>컴포넌트</code> 를 제작해 나갔습니다.</p><details class="details_rxUF alert alert--info details_PYSW" data-collapsed="true"><summary>좋아요 버튼 목록에 표시할까 말까 고민의 흔적</summary><div><div class="collapsibleContent_oXwz"><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAz0lEQVQImSWMy27CMBBF/dt8DWTHV8ACiVWEkrrOo1mEsqCiTuOXktgzcQZZPburc3VYXhR7XtWdqNqS1x2vmpaL5ot/iIaLui0/Rd3dinLPrHPXeV5oHAa6vyxdWk3Fw9G3nMj7QCEAGWOvbF3XMxGRsxb/tIvPwcTJQ1zjFokIkwOAMwshnGKMtCKE0S14fxmcPCJtG8a4hXT03p8YIuZpJOyMJM1CbgYKmIL/AEDOpJQ7pfTRWXNo+md2KfuseoxZ/6MypdRBa338lXL3BuPT21iVfmQeAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="4320" height="2566"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/14.98553d1.4320.png" srcset="/assets/ideal-img/14.98553d1.4320.png 4320w" width="4320" height="2566"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA5ElEQVQImRWMQW6CQBRAWZdh5sMMHxiglkpjYqJtV12575W8idIJMMaFp9CdKzeGpScxE/Iblu/l5XlN13etPT4aY+87cxga0w2mPw5Nexh2pr/v/7rH3rSdJ5S+RlhS8TonrNekf34p+dyQmq8JVE6gNGE2u3o+V5cgTCgrKpfNFqOuV2OYvo1c6pEBOl/ExKPk4jHAM49SApk9VfHh9OLbQTpzAaALwuQ5TUBm5ym8TeBzRTKvCaslyfydAEuaPAMkIdObx0S8ZYCnlyDqsVra8mtjRbWyXNeWQdwzEZ9ElGz/AX9TVB6I2tOpAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="4320" height="2566"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/15.ba4ae65.4320.png" srcset="/assets/ideal-img/15.ba4ae65.4320.png 4320w" width="4320" height="2566"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT" style="width:200px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAWCAYAAAD5Jg1dAAAACXBIWXMAABYlAAAWJQFJUiTwAAACW0lEQVQokV2S208TURDGzz+miT7xZCRIgEh88E8imoAQVG765CteEGupQLmEWy32sr0tbffSltKFvZ7dn9kFUZlkMpM53znffGdG3H84xKPHI4yPP+H5s2EmJ58yMTHG2Ngo4+OjDI+McO/BEGJ69jVTL6aZm3/DwtIKC4tLLL97z9LyIjOzr3g5M8/c/FsEgDM4x7my6PV6nPd6/LEr273NhZSSQEa4QYgXhPgyxPODxF0/wPH8JIp6LyCjuHz+5ST+veyymr+OqVJcd9lreIhTzWdDccnWPD6e/gWsFVw+HNlJ1C2JKOg+Xws2OzWH1bxNuuywW3eT2reinbDFrKLchTUFDjQ4asNBGw41kLcybsQo1Robm9tsZ3cpFMtsZvfY2T+kVCzwY+eQlYxG1XAQrVaLXO6EfD5PRVEoFgqUSyWUcpmTfJH0sUrTvED0+300XaetaTSbLUyzQ6vdRj07wzB0WmoNa3CB6Ha7KJUK1WqNSqWKolSIWUzTJD7TNB3bcRB+EOC4Pp7n4bkuMvDvyLg2YV8OQFq4QYTthfSuQi5dSRRFt04UIQ5qNlNfLFZzPlKGCTgeI1yDwjAijIFuAKYVYbkRd80LouSzO5dhvD3/AuL8/wu3L8ZJvDWBDJFhmNDH+ZUXu0SG1y2IUiciXY3YqEVkqpKsGpGphWzVQ/abEUHcbqw6Htvaeor1VJqTXJ5UOkNmK0vu5JhPqW3m1ltofR9hGAalYpFGo47aaNBoNFBVlTNVpVKt81NpoRkdxMCyiMG6btBsNm/GaCZj1HWdrmnQ6XT4DXKIHauBxy7ZAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="770" height="1682"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/16.aa89a38.770.png" srcset="/assets/ideal-img/16.aa89a38.770.png 770w" width="770" height="1682"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT" style="width:200px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAWCAYAAAD5Jg1dAAAACXBIWXMAABYlAAAWJQFJUiTwAAACdklEQVQokU2TW1PaUBSFz1trhdwvJAQEEyQIIUCiIigXkWqr1XoZH1qn09qq7UPf+0v6d79Owkzbh+/cZu29H9Y64uzylg/3n3j89pmfP+55en7i+ekbj48PPD1+5cvDA28vbhHJcEo6nDKeLDhanubMFifM5kcMRyPi9ICd/Qmi6m8TRT2arS5Rf0g73sX2fHR7A930ePFSpiBbCEkrIWkOklFG0l0k3UHOcf+7uwi1HmHtnlI6vMrJz5Mb7P3zFQfvMQcLhB7uYo8usHZPcOZ3lMaXq6LJNe7yntL0FqXWRujNFGdyhZ0e42bC0TlWssA5vMKZXGOP3qEFfYTcSNAnd8i9BUo8Q+kfo/SOWNPKvCgarBdUCoqJ8Fsx4/kJ49mSOB2RjufsHC6Iewnp4TEbl9/RGgmi5ockO0MG6ZAoTul0B0RxQidOiNIx/vyGUiNGuNWAza0O/laHRqtLvbFN0IxotntsBtu0W23KlTrCqzVodxPaccp2NyHq7RCEETW/RaW+hd+MMEpVhGKUUa0qmumhmWVUw2GtoLFW1HklGfm+LpsIy/GoeQ6K7aE6ddRKE83dpKja/1AsxEbvDYP735QPnpHNlVixKv9EWilHSGYVxYuQbJ+CbK5QrJyiUUZrDFAqIaIgG6wXFQqSno/IyIWykRflHVUbkS15cgyXYpYiPUuSi+zUkUu1/C0rFGozxRy/x9i/wNx/hzE8w9hbofcXSKaXTxNhp8/y9Izl6TnD8ZSD2TGTo9fs7Y2Ynl3jf/yFXOsgvI2AVqdP0OzQCCP8rfZfglZMvbtH9guEXa7ntmX2ZbaFmXWZKIxyK/0gJMvDHySAQ4RzxBCRAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="776" height="1686"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/17.6d95818.776.png" srcset="/assets/ideal-img/17.6d95818.776.png 776w" width="776" height="1686"></noscript></div></div></div></div></details><p>독립적인 글의 형태를 가지는 <code>블로그</code> 와 연속적인 <code>시리즈</code> 의 글을 분리할까 말까 도 고민하며 디자인을 잡아갔습니다.</p><p>이에 대한 내용은 하단에서 기능을 소개하면서 자세히 풀어볼게요!</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="ui">UI<a class="hash-link" href="#ui" title="제목으로 바로 가기">​</a></h2><p>완성된 블로그에서 특징적인 <code>UI</code> 에 대해서 소개합니다.</p><p>하단부터 <code>UI</code> 이외 <code>UX</code> , <code>Feature</code> 으로 <code>섹션</code> 에 따라 모아서 나열했는데 대부분은 <code>Docusaurus</code> 의 <a href="https://docusaurus.io/docs/swizzling" target="_blank" rel="noopener noreferrer">swizzling</a> 를 이용해서 제가 <code>커스텀</code> 한 부분들입니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="모든-페이지에-bttb-버튼-적용">모든 페이지에 <code>BTTB</code> 버튼 적용<a class="hash-link" href="#모든-페이지에-bttb-버튼-적용" title="제목으로 바로 가기">​</a></h3><p><code>Docusaurus</code> 에는 오른쪽 하단에 표시되어 최상단으로 올라갈 수 있는 <code>BTTB(BackToTopButton)</code> <code>컴포넌트</code> 가 있습니다.</p><video width="100%" controls=""><source src="/assets/medias/1-dd856fe04e5a4dd5fbc6117731596f99.mp4"></video><p><code>docs</code> 에는 기본적으로 적용이 되어있는데 <code>blog</code> 에는 적용되어 있지 않아 기본적으로 적용된 <code>DocPage/Layout</code> 부분은 제거하고 전체적으로 반영되는 <code>Layout</code> 컴포넌트를 통해 모두 적용할 수 있었습니다.</p><p>왜 <code>blog</code> 에는 <code>BTTB</code> 가 적용되지 않았는지 <a href="https://github.com/facebook/docusaurus/discussions/7516" target="_blank" rel="noopener noreferrer">Why does BackToTopButton exist only on Docs Page? · Discussion #7516 · facebook/docusaurus</a> 에서 물어보기도 했습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="검색-창을-가운데로">검색 창을 가운데로<a class="hash-link" href="#검색-창을-가운데로" title="제목으로 바로 가기">​</a></h3><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAKklEQVQImRXBQQ4AIAgDQf//WoVSOZCscWZZiVR0N+XLDnFC2M03M2QmDxoAJtaoZhjnAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1065" height="59"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/18.631b875.1065.png" srcset="/assets/ideal-img/18.631b875.1065.png 1065w" width="1065" height="59"></noscript></div></div><p><code>VSCode</code> , <code>YouTube</code> , <code>Notion</code> , etc 뭔가 통합된 검색은 <code>중앙</code> 에 있는 것이 이쁘더군요.</p><p>기본적으로 오른쪽에 표시되던 검색 창을 가운데로 변경했습니다.</p><p>별거 아닐 줄 알았는데 <code>GNB</code> 부분이 이미 <code>Docusaurus</code> 가 할당한 영역들이 많아서 여기를 비집고 들어가 <code>반응형</code> 을 잡느라 애먹었습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="scroll-progress-bar-추가">Scroll Progress bar 추가<a class="hash-link" href="#scroll-progress-bar-추가" title="제목으로 바로 가기">​</a></h3><video width="100%" controls=""><source src="/assets/medias/2-88c2884d64ca6c630242f51c0f0ffe19.mp4"></video><p>자칫하면 사용자 시선 처리가 분산되는 효과를 나타낼 거 같아 넣을까 말까 고민했지만 그래도 적용된 것이 더 모던한 사이트라는 경험을 받을 수 있을 거 같아 추가했습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="ux">UX<a class="hash-link" href="#ux" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="방문한-분들에게-하트-표시">방문한 분들에게 하트 표시<a class="hash-link" href="#방문한-분들에게-하트-표시" title="제목으로 바로 가기">​</a></h3><video width="100%" controls=""><source src="/assets/medias/3-139107c53a639fd8baed476b1ccf67d5.mp4"></video><p>제가 가장 공을 많이 드린 <code>UX</code> 디자인입니다.</p><p><a href="https://hits.seeyoufarm.com/" target="_blank" rel="noopener noreferrer">hits</a> 를 통해서 <code>조회 수</code> 표시와 함께 봐주셔서 감사하다는 의미로 <code>하트</code> 를 표시했습니다.</p><p>페이지를 들어오자마자 <code>하트</code> <code>애니메이션</code> 으로 눈길을 사로잡고 이 특징이 제 블로그를 기억하는데 더 도움이 될 것이라고 생각했습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="medium-zoom-을-이용하여-이미지-확대를-쉽게"><code>medium-zoom</code> 을 이용하여 이미지 확대를 쉽게<a class="hash-link" href="#medium-zoom-을-이용하여-이미지-확대를-쉽게" title="제목으로 바로 가기">​</a></h3><p>블로그를 보면서 가장 많이 보게 되는 것이 <code>텍스트</code> 와 <code>이미지</code> 입니다.</p><p>개인적으로 이미지 과정이 부드럽지 않으면 글을 읽으면서 불편함이 느껴지더군요.</p><p>그래서 <code>medium-zoom</code> 을 이용해서 부드러운 애니메이션과 함께 이미지를 확대할 수 있도록 했습니다.</p><video width="100%" controls=""><source src="/assets/medias/4-dd8206e77b53bfa672d857de8c5afb94.mp4"></video><p>해당 기능을 구현하면서 만난 이슈로 스크롤에 따라서 <code>GNB</code> 를 숨기도록 되어있는데 <code>GNB</code> 가 <code>medium-zoom</code> 를 이기는 현상이 있어서 <code>z-index</code> 으로 해결할 수 있었습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAgUlEQVQImTWMzQ4CMQiE965G49mNG39aLRQK3fd/tTGl8TBhBj5msb5D1VClhbgqqiiqNojOnXnH4mYwM4gomAXlSyiFQYXBXOM2tLg7VBqujw3nz4ZTXnF43nB8rbi871EwmADHRyZCroQkhEQFiWe2ZhMc0DDdO3pMxx5+5n/RDx9eTAjIicmHAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2558" height="1357"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/19.92a7b63.2558.png" srcset="/assets/ideal-img/19.92a7b63.2558.png 2558w" width="2558" height="1357"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="연속되는-글은-시리즈로-분리">연속되는 글은 시리즈로 분리<a class="hash-link" href="#연속되는-글은-시리즈로-분리" title="제목으로 바로 가기">​</a></h3><p>하나의 <code>Article</code> 이 아닌 연속되는 글들이 있습니다.</p><p>예시로 <a href="/series/build-server-with-vm-and-manage-with-docker/">VM으로 Server를 구축하고 Docker로 관리하기</a> , <a href="/series/from-jsp-project-setup-to-deployment/">JSP Project 설정부터 배포까지</a> , <a href="/series/workflows-with-aks-github-slack/">aks, github, slack으로 워크플로우 구축하기</a> 가 있습니다.</p><p>원래는 각각의 <code>Article</code> 마다 상단에 연결된 글의 링크를 <code>TOC</code> 으로 표시하고 있었는데 일일이 업데이트하는 것도 귀찮고 <code>TOC</code> 만으로는 이 글이 <code>시리즈</code> 글인지 알 수 없다고 생각했습니다.</p><p>마침 <code>Docusaurus</code> 에는 <code>docs</code> 라는 기능으로 왼쪽에 <code>Nav</code> 를 표시할 수 있어서 각 글들의 <code>하이어라키</code> 를 나타내기 좋다는 생각을 할 수 있었습니다.</p><div class="imgContainer_g3cu spacer_xInT" style="width:200px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAUCAYAAAC07qxWAAAACXBIWXMAABYlAAAWJQFJUiTwAAABjklEQVQokXVS2W7cMBDT//9aURToc5PFZp09bF2jwzosmcXISZC2qQDCgkFxhpwRa/CQ9ytCjCAikHMopSLnDO89WmuIMULkGCBvF1ymVzw9PQ9Ya+GcxzRNqLVCaQ2xtYY1Jfzv7Ps+vqLWDYoCcu1ofUfZOih1hNIHidF7hzDrju9Tw8/rhm8vFT9eN9xcB/9/k0QpBaJuDWe54rQknOUB5RJmSlgowcWEkjPE3humxeF0JzzfCb+uhPMScJoDJhmhKSKtKwSrO+dgjYHWCkpJ8OO/j+BmnQ+w5AY4FmMJvR9G3jEUtbF4zBKLVLjeHggh/hvPvu8g56G1gdIGy6LgfRjKIR4PhuIozURjYS19lGfVlPKfRFaTSg8wWSmNbdtG0DzrT2Y8iByMtUOZwWQpFWJcD+JnM+v69cw/XLPae498Z3OWaJjjlRuKrfdB4rKMx7xAm6Nnds+9MkSp9ehL82TMyJKV5kWOCmxoEFPOuN0fOJ0vYyJclsEG2RAv7tgefsF5cW58/8oIm/kNlJ4FEQHJMZMAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="592" height="1154"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/20.342a519.592.png" srcset="/assets/ideal-img/20.342a519.592.png 592w" width="592" height="1154"></noscript></div></div><p><code>docs</code> 으로 분리하면 <code>Archive</code> , <code>Tag</code> 가 <code>blog</code> 와 따로 관리된다는 점이 아쉬웠지만 제 블로그를 접속했을 때 왼쪽에 <code>Nav</code> 만을 보고 글이 시리즈 글로 이어진다는 것을 한눈에 파악하고 클릭을 유도하는 것이 더 가치 있는 것이라고 생각하여 <code>연속되는 글</code> 은 <code>docs</code> 라는 기능으로 <code>blog</code> 와 따로 분리했습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="작성자는-하단에-배치">작성자는 하단에 배치<a class="hash-link" href="#작성자는-하단에-배치" title="제목으로 바로 가기">​</a></h3><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAS0lEQVQImRXFuw2AMAxAwew/DgUSk9DRQYGAJv4ksdFDXHNlmSfWbed4HHOji6AiiCq1CqqKu1Pu68S8YW3QR/BmkBFEJvEfQWbyARN4TL2TlI+4AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1146" height="200"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/21.f310bc4.1146.png" srcset="/assets/ideal-img/21.f310bc4.1146.png 1146w" width="1146" height="200"></noscript></div></div><p><code>Docusaurus</code> 는 기본적으로 상단에 <code>작성자</code> 를 표시해 주고 있었는데 이는 <code>커뮤니티</code> 사이트에서 여러 사람의 작성자가 있는 경우 의미가 있는 배치라고 생각했습니다.</p><p>저는 <code>개인 블로그</code> 라 작성한 사람은 나 혼자일 것이고 굳이 상단에 표시할 이유는 없다고 생각되었습니다.</p><p>또한, 글의 퀄리티가 좋다면 글을 끝까지 읽을 것이고 제일 하단에서 글을 작성한 내 정보를 보여주는 것이 더 좋은 <code>UX</code> 라고 생각하여 하단에 배치하는 것으로 바꿨습니다.</p><p>이걸 바꾸면서 모든 페이지에 전역으로 작성자 <code>메타 데이터</code> 를 추가하면 되겠다 생각이 들어 그렇게 처리했습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="feature">Feature<a class="hash-link" href="#feature" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="layout-shift-방지-및-썸네일-이미지-불러올-수-있도록"><code>Layout Shift</code> 방지 및 <code>썸네일</code> 이미지 불러올 수 있도록<a class="hash-link" href="#layout-shift-방지-및-썸네일-이미지-불러올-수-있도록" title="제목으로 바로 가기">​</a></h3><p>이제 어느 정도 <code>UI/UX</code> 가 잡혀가니 아쉬운 <code>기능</code> 과 <code>퀄리티</code> 가 눈에 들어오기 시작했습니다.</p><p>가장 거슬렸던 부분은 <code>이미지</code> 때문에 <code>Layout Shift</code> 현상이 발생하는 것이었는데 이는 <a href="https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-ideal-image" target="_blank" rel="noopener noreferrer">📦 plugin-ideal-image</a> 를 사용해서 해결할 수 있었습니다.</p><p><code>PlugIn</code> 이 있어서 손쉽게 해결될 줄 알았지만... <code>ideal-image</code> 와 <code>medium-zoom</code> 를 통합해야 하는 상황이라 이것도 고생 좀 했습니다.</p><p>만난 문제들은 아래와 같았습니다.</p><details class="details_rxUF alert alert--info details_PYSW" data-collapsed="true"><summary>트러블 슈팅1: <code>docusaurus-plugin-image-zoom</code> 사용 시 중간 이미지부터는 zoom 안되는 현상으로 컴포넌트 제작</summary><div><div class="collapsibleContent_oXwz"><video width="100%" controls=""><source src="/assets/medias/5-0-1-3628d491a43329a9514b33368650e98b.mp4"></video><p>위의 영상을 보면 첫 화면에 로드된 이미지는 <code>미디어 줌</code> 이 되지만 하단에 <code>lazy</code> 로드된 것들은 <code>미디어 줌</code> 이 안 되는 것을 확인할 수 있습니다.</p><p>이유는 <a href="https://github.com/gabrielcsapo/docusaurus-plugin-image-zoom/blob/main/src/zoom.js" target="_blank" rel="noopener noreferrer">docusaurus-plugin-image-zoom/src/zoom.js</a> 를 보면 <a href="https://docusaurus.io/ko/docs/next/advanced/client#client-module-lifecycles" target="_blank" rel="noopener noreferrer">onRouteUpdate()</a> 가 되고 <code>.markdown img</code> 만 적용되기 때문입니다.</p><p><code>docusaurus-plugin-ideal-image</code> 를 사용했기 때문에 화면에 표시되기 전까지는 <code>lazy loading</code> 되어 <code>img</code> 가 아닌 <code>svg</code> 으로 표시되고 있기 때문에 <code>docusaurus-plugin-image-zoom</code> 를 이용해서는 <code>미디어 줌</code> 을 적용할 수 없었습니다.</p><p>때문에 <code>svg</code> 에서 <code>image</code> 으로 바뀌면 해당 부분만 <code>미디어 줌</code> 을 적용하도록 해야 했으며 <code>ideal-image</code> 와 <code>미디어 줌</code> 이 가능한 <code>컴포넌트</code> 를 새롭게 만들었습니다.</p><p>여기서 <code>ideal-image</code> 에서 <code>lazy loading</code> 이 완료되는 <code>onLoad CallBack</code> 이 없어서 구현하는데 머리 좀 싸맸습니다.</p><p>저는 <code>imageContainer</code> 를 만들고 하위 자식이 변경 시 <code>onLoad</code> 되었다고 가정하고 <code>미디어 줌</code> 을 적용하도록 만들었습니다.</p></div></div></details><details class="details_rxUF alert alert--info details_PYSW" data-collapsed="true"><summary>트러블 슈팅2: <code>@theme/IdealImage</code> 타입 에러</summary><div><div class="collapsibleContent_oXwz"><p><code>yarn berry</code> 에서만 발생했던 문제로 <code>import Image from "@theme/IdealImage";</code> 시 관련 컴포넌트가 없다가 에러가 발생했는데 이는 <code>피어 종속성</code> 인 <code>prop-types</code> 가 없어서 발생하던 문제이었습니다.</p><p>그래서 <code>import Image from "@docusaurus/plugin-ideal-image/lib/theme/IdealImage";</code> 시 타입 에러는 표시되지 않았습니다.</p><p>근데 매번 저 이상한 경로로 <code>import</code> 를 하기에는 싫기에 <a href="https://github.com/facebook/docusaurus/blob/main/packages/docusaurus-plugin-ideal-image/src/plugin-ideal-image.d.ts#L53-L72" target="_blank" rel="noopener noreferrer">@theme/IdealImage</a> 을 제가 직접 끌어와서 처리했습니다.</p></div></div></details><p>해당 기능이 가장 <code>블로그</code> 의 본질적인 <code>기능 개선</code> 이었고 사용자가 보기에 모던한 블로그 경험을 제공하는데 중요한 역할을 할 수 있었습니다.</p><p><strong>🌟 앵커 가 제대로 작동</strong></p><table><thead><tr><th>before</th><th>after</th></tr></thead><tbody><tr><td><video width="100%" controls=""><source src="/assets/medias/5-1-1-9f9feca89be7e202eca8a7c826a6f05c.mp4"></video></td><td><video width="100%" controls=""><source src="/assets/medias/5-1-2-0b34f7a7ed6bc3b14c33e9115b1841b0.mp4"></video></td></tr></tbody></table><p><strong>🌟 스크롤 해도 <code>CLS</code> 발생하지 않음: 오른쪽 스크롤 바를 잘 보세요. <code>Page Down</code> 으로 동일하게 내려가는데 레이지 로딩은 계속 불러오면서 스크롤바가 밀리는 것을 볼 수 있습니다.</strong></p><table><thead><tr><th>before</th><th>after</th></tr></thead><tbody><tr><td><video width="100%" controls=""><source src="/assets/medias/5-2-1-8379727f502692de22500b693f7e1081.mp4"></video></td><td><video width="100%" controls=""><source src="/assets/medias/5-2-2-3d48f51578303853a3ea891913b0fad7.mp4"></video></td></tr></tbody></table><p><strong>🌟 이미지 썸네일 표시</strong></p><table><thead><tr><th>before</th><th>after</th></tr></thead><tbody><tr><td><video width="100%" controls=""><source src="/assets/medias/5-3-1-1ee2aa6f888702898b3b892474bed630.mp4"></video></td><td><video width="100%" controls=""><source src="/assets/medias/5-3-2-43c32e43947245b7c60864e2233e17a1.mp4"></video></td></tr></tbody></table><p><strong>🌟 네트워크가 느린 경우 선택적으로 다운로드할 수 있도록</strong></p><table><thead><tr><th>after</th></tr></thead><tbody><tr><td><video width="100%" controls=""><source src="/assets/medias/5-4-1-5fb67890643f32ca092a0551af42bafd.mp4"></video></td></tr></tbody></table><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="홈-화면-인터랙티브-요소interactive-elements-배치">홈 화면: 인터랙티브 요소(Interactive Elements) 배치<a class="hash-link" href="#홈-화면-인터랙티브-요소interactive-elements-배치" title="제목으로 바로 가기">​</a></h3><p>밋밋한 홈 화면보다는 나만의 <code>아이덴티티</code> 를 가지는 화면이 가지고 싶었습니다.</p><p>그래서 사용자의 관심을 끌고, 상호작용을 유도하며, 웹사이트에 머무르게 할 수 있도록 <a href="https://github.com/agile-ts/agile" target="_blank" rel="noopener noreferrer">agile-ts</a> 를 참고해서 <code>인터랙티브 요소(Interactive Elements)</code> 를 홈 화면에 배치했습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAcklEQVQImVWNQQqDUAxEvf9FuvcI0o0FQQq2m3ahiAstorTW7zeTKfkVwQeBDPNIIhEhVGmIYBsJ2VBVAmBkwS8L+7bbS+OWZyyfj10O4jg7pvWL18bz/fWhPMcnFmkSdkD+4gTw/nG8VI7DtB4u21vjB9Pvmskc2lxBAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3552" height="1590"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/22.da25d78.3552.png" srcset="/assets/ideal-img/22.da25d78.3552.png 3552w" width="3552" height="1590"></noscript></div></div><p>이것을 만들면서 겪은 문제는 <code>인터렉션</code> 기능인 <code>이모지</code> 가 넘어가는 것이 <code>모바일</code> 에서는 간헐적으로 로드되지 않는 것을 보았습니다.</p><p>필요한 이미지가 로드되지 않아서 발생한 문제로 확인하고 필요한 이미지를 <code>Preload</code> 하는 방법으로 해결했습니다.</p><p>그래도 조금 <code>플리커링 현상</code> 이 있긴 하지만 이전처럼 이미지가 로드가 안되는 문제는 발생하지 않게 되어 일단 여기까지 하고 나중에 더 디테일을 잡으려고 합니다.</p><table><thead><tr><th>before</th><th>after</th></tr></thead><tbody><tr><td><video width="100%" controls=""><source src="/assets/medias/6-1-2eeb2420ee07fed46f64a838dd5faa31.mp4"></video></td><td><video width="100%" controls=""><source src="/assets/medias/6-2-5124e60a2fd954cb3c97eacb00b73810.mp4"></video></td></tr></tbody></table><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="공유-및-좋아요">공유 및 좋아요<a class="hash-link" href="#공유-및-좋아요" title="제목으로 바로 가기">​</a></h3><p>글 하단에 글이 좋은 경우 <code>Facebook</code> <code>Like</code> 를 클릭하고 손쉽게 <code>공유</code> 할 수 있도록 버튼을 만들었습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAKElEQVQImWP49+/ffxD+///f/1vPfv0/c+vz/5M3P/1//eknWAwmDwAVXyaUXaFpIwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1514" height="244"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/23.546d979.1514.png" srcset="/assets/ideal-img/23.546d979.1514.png 1514w" width="1514" height="244"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAT0lEQVQImTXBuw2AIBRAUfafQldwCgsbFAMSkRcbJzAQPrlWnqNcPEj9hQYlF3rttNIoufJLKaOGaWSRGRss2mtMMJhzY/UaJzvyXES5+QAQjEnGwjJm7QAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="652" height="160"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/24.547eeba.652.png" srcset="/assets/ideal-img/24.547eeba.652.png 652w" width="652" height="160"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="other">Other<a class="hash-link" href="#other" title="제목으로 바로 가기">​</a></h2><ul><li>댓글 기능</li><li>홈 화면 방명록</li><li><code>PWA</code> 지원</li><li><code>태그</code> 와 같이 <code>이모지</code> 추가 가능한 부분은 <code>이모지</code> 배치</li><li>준수 사항 기능을 사용해서 더 알록달록 명확해짐<div class="imgContainer_g3cu spacer_xInT" style="width:250px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAANCAYAAACQN/8FAAAACXBIWXMAABYlAAAWJQFJUiTwAAABPUlEQVQokT2Q226DMBBE/TutmhtgwDa+YMKdkJCQqlEe+v9fMZU3Sh+ORrJ2Z8bLyuOEbrqhHa+Yrw+M8zeW9YnL+sT95xfz8oAyDZjUDTJVYRcr7OIC20hhG0nSzV7gc5vi44uDJbnDsTujrE8wfoB2HXTZw1YjuChx4AabgwTLZEWxVXtGKj3xThG6hjQNLbCwWXcXdOMN/bRSx8Aw39EMC5p+gSl7sLBFg9ONHgPWD3DVSBWChhRW2Pb/8d0xSu0/B67pkyxMJ5lDnFkiyUuC+goPnpcEdexPK9rhCt/M5Fq4DuG+gdBP2RZM2eZ1EtdB6prcef5KiFKDVJTYRSoMtuQUzhM0JLjjRBrcfXMCFx7MmhqV7wnONZKkIDipQhwrRJEAc8JApAVUplEIiyJ/oYVDFkvE+xzpLsUfbB/jRqPIaY4AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1202" height="1556"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/25.fc59157.1202.png" srcset="/assets/ideal-img/25.fc59157.1202.png 1202w" width="1202" height="1556"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT" style="width:250px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAYAAAALpr0TAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA60lEQVQYlUWOW07DMBBFvQGEQsnDz9ixndgRSREfFAoCUiqQ2P9+LvKA4OPoWLrXM8OGkDHmBWNaMOUVad5jmlf4cYbQA65qgeqagynj4UKiUr65hY8zVB9QdwoNN+DKkZk0HkPMcD7B+gTdBwjl0EmLVhi0oiczaQYKW27oZwm4+i/taoHLqgEzNsKFTNPK5H4Y6ZRye3HJd40E65SDcSOhbYSyEcJ4epf1F1VDMCkNtLIw2pELSvZkzjXaRqBrJdj+5RPp6Qx//4pwePtlI8fDhvh4Qrg7gi3PH1i3rz+m4xnx4USFH79T8Rsw1X+82N7ecQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1524" height="1412"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/26.464266b.1524.png" srcset="/assets/ideal-img/26.464266b.1524.png 1524w" width="1524" height="1412"></noscript></div></div></li><li><code>docusaurus</code> 기본 번역이 이상한 부분 수정<ul><li>블로그 <code>이전</code> 과 <code>다음</code> 번역이 반대로 되어있음<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAUklEQVQImT2L2wqAIBQE/f//DAuisLwe9Uxk5MKw87Br7LZznA5VxTlHiImUPnLOs00uQu9Kq5XFrhRptCqIyDj/MdNQoo/4WGldx+gllc59BR4OcnVTh1WFlgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2178" height="696"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/27.d52f131.2178.png" srcset="/assets/ideal-img/27.d52f131.2178.png 2178w" width="2178" height="696"></noscript></div></div></li><li><code>태그</code> 부분에서 같은 글자가 2번 반복됨<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAATElEQVQImUXIMQqAIABAUQ+gGahLbUFZpqJGdv+T/aCl4S1PrP7EHxkfMiFVWm7UWHiuTk2F3m62PSLcvKDthNQGNTqktr/BoLT9/gWLOxtYmh3hgAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1466" height="278"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/28.9898116.1466.png" srcset="/assets/ideal-img/28.9898116.1466.png 1466w" width="1466" height="278"></noscript></div></div></li></ul></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="후기">후기<a class="hash-link" href="#후기" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="블로그-개발-후기">블로그 개발 후기<a class="hash-link" href="#블로그-개발-후기" title="제목으로 바로 가기">​</a></h3><p><code>Docusaurus</code> 라는 <code>Framework</code> 위에서 개발되었지만 나만의 <code>아이덴티티</code> 를 위해서 어떤 <code>UI/UX</code> 를 디자인할 지 고민하는 과정이 재미있고 유익했던 거 같습니다.</p><p>단순하고 쉬운 기능일지 모르지만 이것을 고민하고 만드는 것이 쉽지 않다는 것을 느끼고 앞으로 다른 사람의 블로그를 방문하면 어떤 기능과 <code>아이덴티티</code> 가 있는지 중점적으로 살펴볼 수 있을 거 같습니다.</p><p><code>yarn berry</code> 를 쓰면서 <code>패키지</code> 가 압축이 되어있어서 빠른 <code>종속성 복구</code> 가 가능할지는 몰라도 <code>피어 종속성</code> 이 고려되지 않은 <code>패키지</code> 의 경우 찾아다니면서 복구가 어렵다는 것과, <code>구현 참조</code> 로 실제 코드를 까보는 것들이 생각보다 불편하다는 것도 경험할 수 있었습니다.</p><p>그리고 이전에 운영하던 <code>블로그</code> 의 글을 <code>마이그레이션</code> 하면서 <code>SEO</code> 가 어떻게 이뤄지는지도 경험할 수 있었습니다.</p><p><code>SEO</code> 를 이해하고 안 하고 생긴 차이에 대해서 기억나는 것은 글의 첫 문단은 주로 <code>description</code> <code>meta</code> <code>tag</code> 으로 들어가서 <code>검색 결과</code> 의 요약으로 표시되고, <code>Header</code> 는 <code>H1</code> 는 문서에 한 개만 존재해야 하는 것이지 <code>H1</code> 을 여러 개 명시해서 <code>섹션</code> 을 구분하는 것이 이상하다는 것, 등을 알 수 있었습니다.</p><p>이러한 구조를 알게 됨으로써 회사 차원에서 보고서를 쓰더라도 어떤 것이 <code>시맨틱</code> 한 것이고, 의미 있는 워딩과 문장인지 고려해서 글쓰기가 가능할 것 같다는 생각을 가질 수 있었습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="docusaurus-사용-후기"><code>Docusaurus</code> 사용 후기<a class="hash-link" href="#docusaurus-사용-후기" title="제목으로 바로 가기">​</a></h3><p><code>문서</code> 작성을 위해서 기본적으로 필요한 <code>컴포넌트</code> 들이 있어서 좋았고 <a href="https://docusaurus.io/docs/swizzling" target="_blank" rel="noopener noreferrer">swizzling</a> 으로 손쉽게 <code>커스텀</code> 가능하다는 것도 큰 장점으로 느낄 수 있었습니다.</p><p><code>SEO</code> 도 잘 처리되어 있어서 <code>리버스</code> 로 제가 어떻게 구현되었는지 살펴보면서 알게 된 것도 많았습니다.</p><p><code>Breadcrumbs</code> 까지 <code>검색 엔진</code> 이 인식할 수 있도록 디자인된 것을 보고 퀄리티에 놀랐습니다!</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA40lEQVQImRXD0W6CMBQA0P7zskXRTbpl2X/4Im9+g76ZJTxIcdkGKCRYZFOptHjpBe+ykxw25i/TyfNbdD+YrO+GrnhwuBiOuXAeuRiM3PWT+xo5I3fKtvlpddZISrdUN5YMdAR4o8beqDZA0CIdT9WKZfvjUpmOLtogAPTW2h6t7Ruw/eFQ4j7PqSjkkmV5uVAGSV10a4xBpRRW1RlrAxjFSSuEoDRNF0yWla+B6AqWuq6jprkSABDYnsqfX5JSkpSFz979gH/G2fwr3s2iZOf9j7ep952k3mbzMQvDcB4EAf8DGc3NM86Z9NEAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="4320" height="2566"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/29.6fccc00.4320.png" srcset="/assets/ideal-img/29.6fccc00.4320.png 4320w" width="4320" height="2566"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>제품을 개발하는 능력은 프로로써 어떻게 보면 당연한 것 일 수 있습니다.</p><p>그다음 중요한 것을 시장에 <code>프로모션</code> 을 통해 알리는 것이라고 생각됩니다. 만든 것을 나만 알고 있으면 예쁜 쓰레기라고 생각됩니다.</p><p>뭐가 되었던 유명해지고 사람들이 많이 쓰기 시작해야 <code>서비스</code> 로써 <code>가치</code> 가 창출된다고 생각합니다.</p><p>이번 블로그 개발도 <code>개인 프로젝트</code> 로써 여러 <code>기술</code> 경험도 해보았지만 <code>넓은 범위</code> 로는 나를 <code>프로모션</code> 할 수 있는 <code>블로그</code> 가 만들어졌다는 것에도 큰 의의를 가질 수 있는 거 같습니다.</p><p>만들 때는 언제 끝나지… 생각이었지만 만들어진 것을 보고 하루에 몇 번씩 방문해서 내가 추가한 디테일을 보며 뿌듯해하는 과정이 <code>프로젝트</code> 를 진행하면서 재밌는 포인트이었던 거 같습니다.</p><p>이상 <code>Docusaurus으로 블로그 개발 및 마이그레이션 후기</code> 를 마치도록 하겠습니다. 읽어주셔서 감사합니다.</p>]]></content>
        <category label="blog" term="blog"/>
        <category label="docusaurus" term="docusaurus"/>
        <category label="migration" term="migration"/>
        <category label="UI" term="UI"/>
        <category label="UX" term="UX"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[GitHub Blog에 Google Analytics 적용 튜토리얼]]></title>
        <id>/2022/11/13/tutorial-on-apply-google-analytics-to-github-blog</id>
        <link href="https://parkgang.github.io/blog/2022/11/13/tutorial-on-apply-google-analytics-to-github-blog"/>
        <updated>2022-11-12T22:34:00.000Z</updated>
        <summary type="html"><![CDATA[블로그를 리브랜딩 하면서 Google Analytics 를 도입하게 되었는데 원리와 함께 왜 이렇게 생성해야 하는지에 대한 가이드 글이 없어서 직접 클릭을 통해 분석한 내용의 글을 작성하게 되었습니다.]]></summary>
        <content type="html"><![CDATA[<p>블로그를 리브랜딩 하면서 <code>Google Analytics</code> 를 도입하게 되었는데 원리와 함께 왜 이렇게 생성해야 하는지에 대한 가이드 글이 없어서 직접 클릭을 통해 분석한 내용의 글을 작성하게 되었습니다.</p><p>처음으로 <code>Google Analytics</code> 를 시작하는 경우 네이밍과 같은 것을 아무렇게나 만들지 않고 도구를 이해를 기반으로 시작할 수 있는 좋은 가이드가 될 수 있다는 생각이 들었습니다.</p><p>깃허브 블로그와 같은 개인 블로그에 <code>GA</code> 를 어떻게 붙이고 왜 그렇게 디자인했는가에 대해 소개해 보도록 하겠습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="tldr">TL;DR<a class="hash-link" href="#tldr" title="제목으로 바로 가기">​</a></h2><ol><li><code>Google Analytics</code> 는 브랜드 및 서비스를 종합적으로 관리하고 insight를 제공할 수 있는 분석도구 입니다.</li><li>서비스의 대한 정보를 가공해서 <code>Google Analytics</code> 에게 전달하면 분석을 통해 <code>insight</code> 를 도출해줍니다.</li><li>우리가 해야할 것은 올바르게 <code>Google Analytics</code> 에게 데이터를 전달하는 것입니다.</li></ol><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="google-analytics란">Google Analytics란?<a class="hash-link" href="#google-analytics란" title="제목으로 바로 가기">​</a></h2><p>서비스 및 브랜드를 위한 종합 관리 도구라고 생각할 수 있습니다.</p><p>필자가 테스트를 위해서 애널리틱스 계정을 둘러보았을 때 엄청나게 방대한 기능을 제공한다는 느낌을 받을 수 있었습니다.</p><p>각종 앱의 대한 insight를 한 곳으로 종합해서 분석 정보를 받을 수 있는 <code>SaaS</code> 라고 생각해도 됩니다.</p><p>앱의 대한 범주는 웹앱과 웹 사이트와 같은 웹 종류가 될 수도 있고 안드로이드 앱 iOS 앱과 같은 네이티브 앱이 될 수도 있습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="애널리틱스-계정과-속성의-관계는">애널리틱스 계정과 속성의 관계는?<a class="hash-link" href="#애널리틱스-계정과-속성의-관계는" title="제목으로 바로 가기">​</a></h2><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAYUlEQVQImX1NWwqAMAzr/Y8qqEydfWkjK8wPYQZC2yQlpCJgFlzuiAg0iCr2o8LMcd0B8wCt84SjVpRtz6lmyZMZqpaPQICWtaTQDBaBmr/hHmxF1OtG6D71448Z/Aqj/QFJEuy7wMLO9QAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="826" height="545"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.8c6595a.826.png" srcset="/assets/ideal-img/1.8c6595a.826.png 826w" width="826" height="545"></noscript></div></div><p>애널리틱스를 사용하기 위해서는 기본적으로 <code>계정</code> 과 <code>속성 및 앱</code> 이라는 것을 알아야 합니다.</p><p>필자가 구글 애널리틱스 글을 볼 때 가장 햇갈리고 이해가 안되었던 부분이 이부분입니다.</p><p>대부분 큰 이유와 설명 없이 계정의 경우 그냥 블로그 이름으로 지정을 하던데 이는 시멘틱한 운영 방법은 아니라고 생각합니다.</p><p>그래서 이부분을 알기 위해서 이것 저것 클릭하면서 많은 실험을 해보게 되었습니다.</p><blockquote><p>해보니까 이해가 되고 쉽더라…!</p></blockquote><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="애널리틱스-계정">애널리틱스 계정<a class="hash-link" href="#애널리틱스-계정" title="제목으로 바로 가기">​</a></h3><p><code>애널리틱스 계정</code> 의 경우 회사 혹은 브랜드 단위라고 생각하면 됩니다.</p><p>예를 들어 <code>parkgang</code> 라는 개인 브랜드가 있다면 하위로 <code>blog</code> , <code>포트폴리오</code> , 이외 <code>네이티브 앱</code> 이 있다면 이런 것들이 <code>속성 및 앱</code> 으로 들어가게 됩니다.</p><p>회사를 예를 들어 <code>NAVER</code> 라고 한다면 <code>속성 및 앱</code> 은 <code>네이버 카페</code> , <code>네이버 웹툰</code> 과 같은 것이 들어올 것입니다.</p><p>구글 애널리틱스에서도 계정 부분의 설정을 보면 <code>계정 엑세스 관리</code> 라고 해서 권한 관리를 폭 넓게 할 수 있는 것을 볼 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAjklEQVQYlW2P0QrCMAxF+//f5B/47utARRFhrJJ265r0SKYDCwYuhMvh3iTMWcgSuT4Tw2NG5kqtlbWTElprmFaOw8LhVBhF8XHfte/BrFFKISeB9oHc28EtyIxgDdYkjJczr/sNzalL3AQENcNqRaaJRQRM8cYO9GoHfUk5Iyn/hb43GqqVGCOq/SO/4BtgexLJcItFigAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1032" height="754"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.dabea74.1032.png" srcset="/assets/ideal-img/2.dabea74.1032.png 1032w" width="1032" height="754"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAKklEQVQImRXFsREAIAwDMfZflaOCNDbmuajROFXYpvWSsS+SSB6xmGvzASVqJziltygdAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3168" height="354"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.efd55ea.3168.png" srcset="/assets/ideal-img/3.efd55ea.3168.png 3168w" width="3168" height="354"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAdUlEQVQImWXN2wrDMAgG4L7/c/ZutOtM4ikm/0h2oLSCIPqpy0EESgkRgXrJ0VNzrI+C5ZUyKDOYZQ7OC6MW1XlowpQzRATRGlrv6N8cYeZ40h+WCUUNahXRPmhCdxw/SKmARWE17hfPcLzeth2FGeoNw1zhG6JM6nZelFuIAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3164" height="1960"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.cdf7cc4.3164.png" srcset="/assets/ideal-img/4.cdf7cc4.3164.png 3164w" width="3164" height="1960"></noscript></div></div><p>위의 Interface만 보더라도 큰 범위내에서 관리되는 영역임을 예상할 수 있습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="속성-및-앱">속성 및 앱<a class="hash-link" href="#속성-및-앱" title="제목으로 바로 가기">​</a></h3><p>앞서 <code>속성 및 앱</code> 을 <code>서비스</code> 혹은 <code>앱</code> 이라고 소개했습니다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>공식문서에도 이와 같이 설명하고 있습니다.</h5></div><div class="admonition-content"><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAALUlEQVQImWN4cv/e/we3b/9/9fTp/7cvnv9/9/LF/3evXv1/9+7d/7dv38JpAPQfJUFB84bnAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1432" height="236"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.b372a20.1432.png" srcset="/assets/ideal-img/5.b372a20.1432.png 1432w" width="1432" height="236"></noscript></div></div><p><a href="https://support.google.com/analytics/answer/9303323?hl=ko" target="_blank" rel="noopener noreferrer">https://support.google.com/analytics/answer/9303323?hl=ko</a></p></div></div><p>그래서 <code>속성</code> 매뉴를 보면 여러 데이터를 가져올 수 있는 기능을 제공하고 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAARCAYAAADkIz3lAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA60lEQVQokYWR3WrDMAyF8/4P1j3B7nox2F0o7ZxKli3bOkNazVZYUoGRcvxFf142yrgmwiVlVG2oqnFIFIl/YtcXWMXHKjidFbUZYAbA8P7JeDsLhhnMDEsfhpQSShG4udh7B9EdIjm+AxxjQEQgpYTg5lrOGbXWP6AZiBmcc0ATTNsW8FNG/9P9FD1m5tBnO8ssN4WZ0dtR1d+M83IKM3bwqfQe6NBL0NdzJ0Jr7RhsfWC9fsV+D0HtA5eNUXygR9ZdcL2lGMjbOCx9SwwmilW5/T/MMGwk8d6+y11wvrVDh6Xd+2pq1fCe8RsgsJ2amNICSwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="680" height="1120"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.a00e786.680.png" srcset="/assets/ideal-img/6.a00e786.680.png 680w" width="680" height="1120"></noscript></div></div><p>해당 글을 보는 분들은 대부분 <code>blog</code> 와 같은 웹 사이트를 중점적으로 운영하실 것이라고 생각하니 위 매뉴 중에서 <code>데이터 스트림</code> , <code>Search Console 링크</code> 는 익숙하실 것이라고 생각합니다.</p><blockquote><p>대충 느낌이 오시지 않나요? 한개의 서비스에 관련된 여러 데이터를 종합해서 볼 수 있다는 것을!</p></blockquote><p><code>데이터 스트림</code> 을 클릭해보면 웹 사이트의 insight를 가져올 수 있는 방법이 가이드 되고 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAbElEQVQImS2MwQ3DMAwDs/+AnaDvNraVSJYlXeEi/BDgHXiYGa/3jVhQlZhNfBpNFFVj87WCw925rpvMZEdEOLvwOQffNuhjkPGIozUigjmdLopHcc3CVmFeZBbHikBV/497mCup2kISmU8XP+lInF3tkubLAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="999" height="420"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.db229be.999.png" srcset="/assets/ideal-img/7.db229be.999.png 999w" width="999" height="420"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAgUlEQVQImT2O4QrCMAyE9/7v6A/BoaLMbe3SNGn9JBUNhLvAx12mokrKmdDWO+4Nb43Wvhq3uTNVc6QUVJXeOzGhZvb3RSuTVkPUWF4r254QKaMhVkpF1AlmgGbO/blynmdOlyu3x0LKB3sW0qHUHxi1WxJyEZLISOj9Pf6MkEj8ACvkwqmP/lIFAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="975" height="544"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.e6d0136.975.png" srcset="/assets/ideal-img/8.e6d0136.975.png 975w" width="975" height="544"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAALCAYAAABGbhwYAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA90lEQVQYlW2QWW7EIBBEOXjulAPlN7PYA6ZZzGY8LwJL8xWkJxB0VxWtzt5YlgXvPf+tn7Xz9e1ROnSWdSWGgPMe59xE5Npj3Al7QbUDXjYRU6W1Sq0XpRRyLvPMu6O2fUN8pLaD1hrHcczH8zw/9sNV6Rh4LgtOBBFhsxZr7bQWcaS0s8WOkpy53R9obZCZ7WoYReNufNKnE/XyJ4/ngjEabczEDLZtFg3FaX10WG2aOUvJpHyRc6b3PrMe/T0yan4fTzaXCDHhQ/gwxpX2iBmKkiq3+x1jPT5EYozsKX0YLi51lIlvrDiCd1grs/CaY51zbLVOxT/h1KdZzD7QQAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="630" height="678"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9.933a55c.630.png" srcset="/assets/ideal-img/9.933a55c.630.png 630w" width="630" height="678"></noscript></div></div><p>정말 많은 정보를 가져오는 것을 볼 수 있습니다. 가슴이 웅장해집니다.</p><p>이번에는 <code>Search Console 링크</code> 를 볼까요? 아마 <code>GitHub pages</code> 으로 직접 블로그를 개발해 운영한다면 익숙한 서비스 일 것 입니다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p><code>Search Console</code> 은 구글 검색 엔진에 등록해주는 도구입니다.</p></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAALCAYAAABGbhwYAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAqElEQVQYlYWPzQrCMBCE8/4v58mDRwVPUqE/mc0mm45sirWKpQMDYfPtTBIiwMsdhBpTShynSIjQrLKYLS6FQTWz60GrM3POlAZZs0uQeDo/GPwSceI8L6CnilukJaeklFQYPBaQBr7lZ3etn1nwCgAruF3gZin4Y71uC/5zq44RrLV+gb8Kosp+GI9BaGY/TrRSDhJz4SDKuvOZFdQIjrcr9dmxITuJL87CsYk6zFJJAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1001" height="1105"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/10.608324d.1001.png" srcset="/assets/ideal-img/10.608324d.1001.png 1001w" width="1001" height="1105"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAoUlEQVQYlT2PCQrDMAwE8/93lqakiev4knzEU+weggWBViPtEmLE2hNrLZoz7bpo7adGqZVcCot1npASokrOGVVF5KNSCqKZ1+k+xpiEbdu4rw9u65PtubPvB8dhCDFx+sByOo+Pym4cxhicD/OFQZtEUQZsEiUXSm2IyDxfa2VUixHxHjuIw5hECMFPQ+/9L3qfi39iTGmGmMNv/fqReBjf8pAP07s29BAAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="974" height="686"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/11.e3d5392.974.png" srcset="/assets/ideal-img/11.e3d5392.974.png 974w" width="974" height="686"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAASklEQVQImSXFUQqAIBBAwe5/uM4QRSiRZYm6q+YL6mOYwbjMYj2zOb/XLWCPhHW//VLGSRmSVLI0sjZCFPydkPIQcyWmgtYOvfMCJCtMpxz4FpcAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="984" height="268"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/12.302d219.984.png" srcset="/assets/ideal-img/12.302d219.984.png 984w" width="984" height="268"></noscript></div></div><p>계정을 클릭해보니 제가 운영하고 있는 블로그가 <code>Search Console</code> 에 등록되어 있다고 표시되는 것을 확인할 수 있네요.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="이쯤-보고-공식-문서-한번-읽어보기">이쯤 보고 공식 문서 한번 읽어보기<a class="hash-link" href="#이쯤-보고-공식-문서-한번-읽어보기" title="제목으로 바로 가기">​</a></h3><p>이외 공식문서에 예시와 함께 아주 잘 설명해주고 있습니다. 보시는 것을 추천합니다.</p><ul><li><a href="https://support.google.com/analytics/answer/9303323" target="_blank" rel="noopener noreferrer">Google 애널리틱스 계층 구조</a></li></ul><p>위의 공식 문서를 읽으면 알 수 있지만 <code>계정</code> 과 <code>속성</code> 의 관계는 자유입니다. 제가 설명한 것은 일반적은 개인 브랜드 범위 내에서 가능한 시나리오 이었습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="이외">이외<a class="hash-link" href="#이외" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="한-구글-계정에서-여러개의-계정-을-만들-수-있어요">한 구글 계정에서 여러개의 <code>계정</code> 을 만들 수 있어요<a class="hash-link" href="#한-구글-계정에서-여러개의-계정-을-만들-수-있어요" title="제목으로 바로 가기">​</a></h3><p><code>Google Analytics</code> 에서 처음에 <code>계정</code> 을 만들라고 하니까 뭔가 한개만 만들 수 있는거 아닌가? 하고 필자는 엄청 했갈렸거든요… 사실 그거 때문에 해당 아티클이 게시되는 것 이기도 합니다.</p><p>결론은 하나의 구글 계정에 여러개의 <code>계정</code> 을 만들 수 있으니까 참고하세요!</p><blockquote><p>걱정하지 말고 <code>계정</code> 을 만들고 테스트를 해봅시다.</p></blockquote><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="유니버설-애널리틱스-라고-또-있던데-이건-뭔가요"><code>유니버설 애널리틱스</code> 라고 또 있던데 이건 뭔가요?<a class="hash-link" href="#유니버설-애널리틱스-라고-또-있던데-이건-뭔가요" title="제목으로 바로 가기">​</a></h3><p>최신 <code>Google Analytics</code> 은 <code>Google 애널리틱스 4</code> 라고 불립니다.</p><p>이전에 존재하던게 <code>유니버설 애널리틱스 속성</code> 으로 알고 있어요.</p><p>속성을 생성할 때 <code>2023년 7월 1일부터 유니버설 애널리틱스 속성이 데이터를 수집하지 않습니다. 대신 Google 애널리틱스 4 속성을 만드는 것이 좋습니다.</code> 으로 안내하기 때문에 기본 값인 <code>Google 애널리틱스 4</code> 으로 만드는 것이 좋을 겁니다.</p><blockquote><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAJElEQVQImWP49vHj/89v3/7/8ekTEv74/+eXz///fPsKxiA2ABSgJpmdQBW1AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2614" height="324"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/13.9503c62.2614.png" srcset="/assets/ideal-img/13.9503c62.2614.png 2614w" width="2614" height="324"></noscript></div></div></blockquote><p>그리고 <code>Google Search Console</code> 에서는 <code>유니버설 애널리틱스 속성</code> 만 연결할 수 있다고 안내되고 있는데</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAcklEQVQImTXKyw4CIRBE0fn/j1RBeTQw4ChMwM01YFx0TlWlt9YaMSZyzpRS2Pefc5t5Wmtl62NgveAkEGJCQsR6T8qFep683pXaGtvonewsuzUk81iKVjzFc4TAESP9/2iuF7xWOHVbyl2vmz2I8BmDL2sElpd1xpXRAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="838" height="346"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/14.8a58277.838.png" srcset="/assets/ideal-img/14.8a58277.838.png 838w" width="838" height="346"></noscript></div></div><p>다시 찾아보니 공식문서에도 지원한다고 설명하고 있네요.</p><ul><li><a href="https://support.google.com/analytics/answer/10737381" target="_blank" rel="noopener noreferrer">[GA4] Search Console integration</a></li></ul><p>또한, 테스트 해보니 <code>Google 애널리틱스 4</code> 에서도 <code>Google Search Console</code> 연결의 완료까지는 안헀지만 마지막 단계 까지는 문제 없이 넘어가졌습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAZ0lEQVQImXWOQQ7FIAhEvf85u+xCQRnF1mlok+YvfkkeG15mSKKVokr3SZ8P84fhzgYwlRBrY86FIkKtRqATwI0BzKJMkVgUbAae58G11ktMNNxiUWUfTrPG0fu3GCt+2fZBn3H8L14HYMM+pZWVTgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3162" height="1642"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/15.984aa7d.3162.png" srcset="/assets/ideal-img/15.984aa7d.3162.png 3162w" width="3162" height="1642"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="만들어진-계정-과-속성-정보를-변경할-수-있나요">만들어진 <code>계정</code> 과 <code>속성</code> 정보를 변경할 수 있나요?<a class="hash-link" href="#만들어진-계정-과-속성-정보를-변경할-수-있나요" title="제목으로 바로 가기">​</a></h3><p><code>계정</code> 이름이나 국가, 데이터 공유 설정 등을 변경할 수 있고 <code>속성</code> 의 경우 이름, 업종 카테고리, 보고 시간대, 표시 통화 등을 변경할 수 있습니다.</p><blockquote><p>근데 역시 제일 중요한건 <code>이름</code> 이 변경 가능하다는 것 아니겠어요? 처음부터 네이밍 걱정하지 않아도 됩니다!</p></blockquote><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="blog에-ga4를-도입해보자">Blog에 GA4를 도입해보자<a class="hash-link" href="#blog에-ga4를-도입해보자" title="제목으로 바로 가기">​</a></h2><p>위의 내용을 종합해서 실제 운영 중인 blog에 <code>Google Analytics</code> 을 도입해보도록 하겠습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="계정-및-속성-생성">계정 및 속성 생성<a class="hash-link" href="#계정-및-속성-생성" title="제목으로 바로 가기">​</a></h3><p>아래의 사진을 참고하여 계정과 속성을 생성하도록 합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAnElEQVQImS3Hy04CMRhA4T6/j+DGlVsSHsTEvQqBoNBq55/epu209BjBk3yLo9q0p8gB8yNorfHeM8ZAZGaappu/V919sSaLD4kQAsuSaa0TY/oX6b2jLt+CuPuc7YL1lVIrZ63JpdCvV3KpKK0NMjtaWahJWJ0hh4C1EZGEk0hbG+rtfcdFG8bg1pgNL6+fPDwfedyeeNp8kFPhF8M5vbnTCDjKAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="616" height="332"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/16.9a7b081.616.png" srcset="/assets/ideal-img/16.9a7b081.616.png 616w" width="616" height="332"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAANCAYAAACQN/8FAAAACXBIWXMAAAsTAAALEwEAmpwYAAABZ0lEQVQokW2Q20ocQRCG59nCyqqrIRHJuwQUfAUFzYUP451ZvAsJDqyr7s6hu5exx+mZne3p6un+pScXHrDgpyjqqwN/tLO7+/Pg8JB9O/jxdzLZj7dG43g0nsRfRtvx9s7ev72v3/Ot8eQ4mk6vfy2SJdKMgTGGLM2R5hxJxrBMUiySFL+nN5fRfH5/CgDGegPAfZAJPSnleXQ3vz9zzmPTrqlVCr0x8M7Bew/nnA0gY+wiWiyTs1B0WlNT16CuA9n+v4hsGBBCvIKGiApZQqkalWpQqRpl1VhD9j1IZElKCVlWKJ8VVL1GWa2t7gyE4K+gNpbqpoUxBl3XDdJa29659xs7Y6mqFDabDfRbsO8//GiIwllVN8NWIhv+tsGRAXxcJIOPD1lj1q1x3vXOez/46P3gLTjn51GWs4tQtK0O6dMoiuIyurr+c8RFseKcx4zzWZaLWcpWsyznMyFE/PQkV7dxfPIC4eTQXLZrmr8AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="3268"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/17.90ddf9e.2384.png" srcset="/assets/ideal-img/17.90ddf9e.2384.png 2384w" width="2384" height="3268"></noscript></div></div><p><code>계정 데이터 공유 설정</code> 의 경우 따로 건드리지 않고 기본 값으로 체크 했습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAABQklEQVQYlV2Q20oCYRSF511CEodUkKKeIXqJoDeRuTVEgiLqJogyYQK7EHVmNM1nsAM0Rs5c1TiHZv7jil8Log2LvWF9e8HeWqlU2t/a3u5XNndaul40c7m8mVsvmGu5vFnQi61iudLTN8oHWvPm+tgaOBgMRxgO7mFbDvrOCD1riL6aLQeXV80zzXXdQ6wqA8D/KVNGEAQNbfr4VCeUYxGllBACxjko42ArMUBiPp8faa+uW1dbJEtpGASIohhhmIAQCs4lU57neQqcLcEsjmn2+QGyCMCTGGAMUv6C/gqUACQjNI1CSQmRQggpobg/ie7sbXnMy3uSxl9MGSpq2aWUqfJ8329o02f3hAuBYJGAUgYhBDj/kRDLd3ied66dXtzuOsNxbTIZVy3bNjpd2+j0Bkanaxm241RH44dau3239w3I7E9mTKuuRgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="2388"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/18.b7eb13d.2384.png" srcset="/assets/ideal-img/18.b7eb13d.2384.png 2384w" width="2384" height="2388"></noscript></div></div><p>알맞게 입력합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAMCAYAAABbayygAAAACXBIWXMAAAsTAAALEwEAmpwYAAABSUlEQVQYlWWQ3UrDQBCF910kaAk2SBX1JQRfQfBlSi6k1IIieiViSyEI6kWbmE1Liz5CRTDxogvWmmq2+d0dmRjQ6sBhGM7HHGaIpml7G5tbZmV9u62qZUNRVgxlWTWWlJJRUsvtslbpqqtr+6TZah5Z1Abb6YNDHbizbDDtPnQtB0zLhq5lw8Vl65S4rnsA3xUBQPZHERq+79fJaPRYi+IU/M8okSIDIQRkqCxDpQiOx+MGeXa9mgSAMIqTOI4hTdMcllJiXwRxCMMo4ZznmxDCklL+B/k8TKbT9xz6pQJkP2Awj5PXyRsEQZBvLeL/g7OPADdKIQTm5lqIdj0vf890xsOAh2ikBYA9RI8xViejJ+8Yh5dJAjOOr5OQCbxYgiiOYoydkZPzqx3aGzQe7gdVSqneManesXp6x7R16jjVwXB4eH1zu/sFpACcV+eJBcwAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="2870"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/19.cb8f12d.2384.png" srcset="/assets/ideal-img/19.cb8f12d.2384.png 2384w" width="2384" height="2870"></noscript></div></div><p>알맞게 입력합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAMCAYAAABbayygAAAACXBIWXMAAAsTAAALEwEAmpwYAAABmklEQVQYlVVP32rTYBzNy7ihs5gJc+jeYeArCN545Z2UDkSyNn/qYJSsWyLJSiBdlz9N/L5M2uLUq/YRtouCeQI3A3ZtvpgjXwWlF4dzcc7v/M4RNkXxxfbTZ5dbT3bChxUxXlt/EK/dr8T31jfijYoYiptbnyuPHr8UqrWaoTQ1qM0DNLUmFFlFQz1AXdZQlxU0FBVvqjVbODk+Poz6EYIwmhNCCkJpQSkpKKUFIWROKYXjOLqg60et3rkHx+3lvu/D87x/8H2fhWEIy7LagmGYLd8PEMcf8+urK0ynU3xPU6RpiiRJWBAEsG27LRjmhxZPukiSfJZlWCwWYEWBsiwxHA4Z11aMSXKR/7i5we3tT2RZhtlshsFgwPi3FSOhn/Is+4XFPEeeFyjYb4xGo/+JJ4bZ4oV7517+5eu3cjwel5PJZMlRFK2MOez3++g47p192mFnZ13mui7rdrs87Y5rlmXpgt42TX5ldjy4vQBcCMO/4IuXRts+FV69ru5KdbmtKrImSdL7t+846kuW9ve1hqwc1fb2nv8BZt5LF7N6tiIAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="2870"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/20.566ff99.2384.png" srcset="/assets/ideal-img/20.566ff99.2384.png 2384w" width="2384" height="2870"></noscript></div></div><p>약관에 동의합니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="데이터-스트림-설정">데이터 스트림 설정<a class="hash-link" href="#데이터-스트림-설정" title="제목으로 바로 가기">​</a></h3><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAMCAYAAABbayygAAAACXBIWXMAAAsTAAALEwEAmpwYAAABWklEQVQYlV2PzUrDUBSE8yxS/KnWFvyhvoTgKwi+TMmihCoooisRqYWgrdE2SX9p38GK0GbR7EzbJDf33uSOJLZoHRjOYj7OmSNls9nTw3ze2D84qqS3M2oqtaGm1tPqWmpT3UpnKru5PX0nkzuTyo/lS6PVhFY3Ua/rqFY1PGs6aq86tDcdRrON+4fyjWSNR0UAcAgCAOE/B0nmOCXpffih+ITA8zzGGIPveZjOXDgzF1/OnFNKMZlMLqTR2FI45wgIYYxSuNMZWMAQMAEWCs4jwF6CYRgioJSFIUcURRBCIIoS8/i0bdu/IKWUxZtjaCkh/oBjy1Jo3M33V8B4roCWZSlewDB3PcY5E7F+uEQrYJHyED4hhHPOF2FiIQRZgCVp+GldMR6BEIK4QvKEQDKXNWzbvpWu756O293+RbfXK5jNltww23LD7MoNoyW3O51CfzA4r71oJ98b7Zu0456OUwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="2870"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/21.5a89fff.2384.png" srcset="/assets/ideal-img/21.5a89fff.2384.png 2384w" width="2384" height="2870"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAABEElEQVQYlV3IsU7CQAAG4HsX02BoamowhhcwvoeJo4sjdpBEd2IIAQzIbppc0dHetYUQX8FFoNChSqUq2F4P+xsdHb7lI7u6frRfLtulvbKpqhpVlAJVCkW6pWzToqqZO3rpQdX0Y3Jerbas+zvYjgfX9eA6Hrg3AnOHYI6HwXAEavW7pNvp1BaLCABSAJt/fg+v0VudtNrXV1N/hkyILHoJ83gZ5+/L5Z/Pj5VMkhTjqd8gvZteLQgCrEWeTIKFDMJQhlEs10kqV19Jmslv+LN5nTSanfZ8PsNzsMbTJMZGCiSpgMgyCCEgRIbxZNolJ6dnB7cmvXwcDSpDjxuMOwbnjsEYN2zGK47rXlCrf/gDCwrVrh2nmVYAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="1664"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/22.e204f6f.2384.png" srcset="/assets/ideal-img/22.e204f6f.2384.png 2384w" width="2384" height="1664"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="blog-제품에-script-추가">Blog 제품에 Script 추가<a class="hash-link" href="#blog-제품에-script-추가" title="제목으로 바로 가기">​</a></h3><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAANCAYAAACQN/8FAAAACXBIWXMAAAsTAAALEwEAmpwYAAABi0lEQVQokVWQ20rjYBSF8xrFEVGYG1GpB4aJtvoEgs8g+C6lV6IdZ0bm8AIW4502bZoU1Jk3UGu9G8QOOTXJf+6/JKkgvfjuPvZaaxvra2v7nz6bnfUNs7mw8NEqlWat0syc9WF23lpcKjdXyhvtpeXVA6OyvfP99OcPdLouWldXBZd2G5ctG7Zto+v1cNY8/2WYpnl4++cvALCxiBRAFUAm6IQBCv+eXxrG5tbWkeN6kEoJQggIoRglCYQQkFLKjFDcPfRPjEq1epSf14DI0gScMVDGJqJSkjKOh8fBuwhA+EGIIIqRphk452BcyoxOiV4hDv0QQRwhv6I1oLWWhLFpUQHieRgiihLE8ajoSRmbjna9HsQYYvjfRzxKkKQZ0qxAZpROi3l0EASaUqY555oyobmYjLl/Ew/dXrGa+r4vU8KlVGOZ9wPyuhz3/UHD2KxUT/PVaqzhhyFGaVZE5/8jlCIlFP3B029jcbm8++Xrt8b1zW2t7XTrE5y63XHqrXan5rje8bl1sfcKY3+evGMoqu8AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="3106"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/23.9d0d2f8.2384.png" srcset="/assets/ideal-img/23.9d0d2f8.2384.png 2384w" width="2384" height="3106"></noscript></div></div><p>이제 <code>GA4</code> 를 생성했으니 데이터를 전송해야겠죠? 웹 스트림의 <code>측정 ID</code> 부분이 중요합니다.</p><p>필자는 <code>docusaurus</code> 으로 blog가 개발되어 <a href="https://docusaurus.io/ko/docs/api/plugins/@docusaurus/plugin-google-analytics" target="_blank" rel="noopener noreferrer">📦 plugin-google-analytics</a> 를 이용해서 손쉽게 추가 할 수 있었는데 각 제품 형태에 맞게 추가하시면 됩니다.</p><blockquote><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAe0lEQVQImTXKyQ6CMBRAUXY2UoZXbEFe6MAQgv//f9cQ4+LsTnWdkVgO0noQtPBsX5jaYeyAsYKxjkfdU42a0JTI20VMB6oZfRem5cRNGa8bblzumIklse8X6/5hCAttIzjx1J3H3vpA5cJMmBU3TEjvERkRCTTdL/59ATlAMqUXiPACAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1082" height="416"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/24.9e5d363.1082.png" srcset="/assets/ideal-img/24.9e5d363.1082.png 1082w" width="1082" height="416"></noscript></div></div></blockquote><p>가장 바닐라스럽게 처리하려면 GA에서 제공하는 스크립트를 사용하면 될 것이고 이외도 관련 많은 패키지들이 존재할 것 입니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="데이터가-잘-들어갔는지-확인">데이터가 잘 들어갔는지 확인<a class="hash-link" href="#데이터가-잘-들어갔는지-확인" title="제목으로 바로 가기">​</a></h3><p>블로그를 한번 접속하고 옵니다. 그런 뒤 애널리틱스에 오면 잘 측정되는 것을 확인할 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA2klEQVQImSWKy07DMBRE/c9ISIEmJIDgP7ppd3xDu0HdsKgsGlUBuxEqQXnYOIXEvte+KGVxNKMzwy6idB6nD0V0FW8vo4RH1wmfJRm/SW95nGTbu/vHYhanc/bMq03ZIB3rgcrPnr4UUm2IKoX0UVs6dkhl9bNhp16vf0ek1jhQxnrVO69P6Ds9+EYP0BpHVdOvWdvp1Thacs5ZBIAJCh7AWkD01gGSUmrFvk3/AuhpEj7QGcBA6MN/94GmD3vd5Zk8lE9CyIWQh+Xbu1juC3FOIeVi2nZ5nv0BYD7PgMXdq6AAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="4320" height="2566"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/25.bd51573.4320.png" srcset="/assets/ideal-img/25.bd51573.4320.png 4320w" width="4320" height="2566"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="search-console-링크-추가">Search Console 링크 추가<a class="hash-link" href="#search-console-링크-추가" title="제목으로 바로 가기">​</a></h3><p>Search Console도 추가해주도록 합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAMCAYAAABbayygAAAACXBIWXMAAAsTAAALEwEAmpwYAAABRUlEQVQYlX2P30oCQRhH980yMq2NIMG3CLrvoneopYyI3qIrydvItKvMXd0/jo7mapCBOju7Mzu/mFXBbjrw8c3A4QxjHJrmealc/jwpld+LRdPO5fJ2bu/A3tkt2PtFs3V0XBrlC+aFUa8/3/qEoOX0EAQEna4Hx+2h6xO4XgBKKRqNxr3h+8GVECm+52kMIBVCpBp9BhCzGHDc0Y3h+YHFOQePWCKlBGMMURSBsQicR+JnHuOjM6gYA0otISQ454nCCqUUdFTKVOj713RSMfoDaulSJiqVSRuUUpk4HocrUWyJm+J6MjEMQ/300OJxgsVi+b9IKbUYF5gvmRa1qdeG7SK9jHVxNuMySaQCpFJqM3wtXhtkMLxLU4U4Yn8+ss1kOn0wHp9eTr2AvPYIqXZdt9Z23Fq742fb9bwqIf16s/l29gvlIql1/kX+lQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="2984"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/26.181f76e.2384.png" srcset="/assets/ideal-img/26.181f76e.2384.png 2384w" width="2384" height="2984"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAABH0lEQVQYlVWMS0sCYRiFv9+mUpN2EYP+RZt2ESEt3amUhVNpuwhrWSniH0jbFAyZc3EuMkIUI813e53vjVlVi4cDh+cckslm94vbpVmxtPOiaQUjl1s1sisFI5PTjHxh83VjqxRoa+uHpFqrng6fR2jaU7QtG30/QC8I0fVn6Ho+ul6AT8ORTvSmXnNcDxWi4JwnnIskTSF4AgAClglazvSE6OcXddOyMWZChuEcZ+EcPz6/MIoijBYL+KYM394nDdJqteum7aCARFLKFKVUAYBCRLWUAJRxHE/MBrlqt6uW7aCUwKSUgIi/KMUZF6l4TM6arWb6GDOGcUzTwT/SfjwxL8nu3oF2fdOp9AeD8kO3d/SX+8duudfvVzq3d/kfAHwCS7UU+YkAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="1968"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/27.72c708a.2384.png" srcset="/assets/ideal-img/27.72c708a.2384.png 2384w" width="2384" height="1968"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA1klEQVQImS3FvWrCUAAG0PtYdgppAyYtrYFCn8Gxj1AXnQLpUlsHwZCtiE8gOjRDwUHjzZ+txCq4ZTA2N8klySctDodDBEF8vFMbu4Z6v5BlhV5dSlSU6lQQJSorN7ZyfbupXYgdounP2sfnDEt3jbntY7EMYHvhP8dfY05D9M3xO3nr9dqr1Q8YB4tZxaNDzuOk5EdW8UNSplkBbPfRgLx0X/Wv7w0yDrCsBMsrsKxCkhb4TQukHAi3+yF5arUeJtPpKAgCg1JqOo5z5v5teJ43tCyreQJMmZdNgrxjFgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1192" height="642"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/28.829ac41.1192.png" srcset="/assets/ideal-img/28.829ac41.1192.png 1192w" width="1192" height="642"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAYAAAALpr0TAAAACXBIWXMAAAsTAAALEwEAmpwYAAABNklEQVQYlU3KPU/CQBjA8ftoBoiAKEQSFxO/hItxMM4yKRRaYkLcWY0huiOkMGnSSNvjrbxoNFKhd9de+zwGwsA/+W1/sheLXeby+Wnu+OQtlTowEvGkEd/PGLFEykhnsu9H2fwkmT68IjeFgtru6Gj3h0hpH53xBMeTOY6cKY7Gzobe6d6TUkm5o/0BAqLPhYi48CPORSSEiKSUvgwjpINhmZSUcrFnWci4H8zmnzibf+H3zy8uFi667p9cMY4fplUhqlYtmpaNQQiBxzi6rotBEOA6AJCMC+xZdoWoqrYZZRgF1FkCYwIAItgmmdiOmqbdmjZFxoXwVixExA0ACBFAbEeFFBWtuh658HHpebj02A4PV4yhadMaOb+4Pn18atT1brfWfG09NFvtXbW23qk3nl/O/gFL3Sg6nz2f4AAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="2238"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/29.6f6691a.2384.png" srcset="/assets/ideal-img/29.6f6691a.2384.png 2384w" width="2384" height="2238"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAYAAAALpr0TAAAACXBIWXMAAAsTAAALEwEAmpwYAAABNElEQVQYlU3Py0rDQBQG4Hm2tmi0XkoEF4LvoaBL923xgk1ts+9eSn2AGtu4UQhqc2kuNYqoSZrMTGaSI8FS/eHnbD7O4aBSuXxY2xFnNXH3QRCqWqWyqpVXqlqpImjr1a3HzW3RE9Y2jlG9Ub8Y36ugm1MwTRNc1wPH88F2Z2A7LtiOB6OxKiGpJTUs24EcgBJCMkJoVkxKScYYo4xnYFjTMyS1r5q6YUKMaer7b/Dqv8PH5xcEQQBBGLJ5guHpZXKOul25aZgWzDFP44RAxjn8JWcJIfA80X+hbljAszzlnOeMsbwQOUBe3E7wAsqyXC82YkIJxoTTtPDLkAU8Ra1297L4OMYEgjCCIJov+x1GEMUJTHSjgw6OTvau+4PeSFU7w1tFHip3/6p0lNG41x/c7P8ATc4pgUq7ZJQAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="2238"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/30.38744fc.2384.png" srcset="/assets/ideal-img/30.38744fc.2384.png 2384w" width="2384" height="2238"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAlUlEQVQImWMw0NOLMjM3321iYrFGS0t3vYqK2noVNc31Sirq67V19Nbo6hvt0NTSzWOoqq3vXbVu3f/tO3b93717z/89u/b837X3wP8du/b937V7z/+9e/f9X7167WqG+QsW9D978eo/PvD6zdu1DBWVlYGr165deuDgoRm79+ydDcZ794HpXXv2zty7/8Dides3JAEAZRJpoOHWUhcAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1192" height="487"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/31.94fa4e5.1192.png" srcset="/assets/ideal-img/31.94fa4e5.1192.png 1192w" width="1192" height="487"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAYAAAALpr0TAAAACXBIWXMAAAsTAAALEwEAmpwYAAABQklEQVQYlV3OvU7CYBgF4O9eBKMgWI0amY03YmJcXFUMC3cgVlLowmaMhkoHBQIU6889KC1lAaxGJIH+t7THVOLi8OQd3pOTQ9Yoam9rOyVsbKa4WDzJR6JLfGQxxi9El/mVBMUlV9db8QS1T04zGZbjK2gKIhqNJtqCCEF8Qav9BKEt4uHxGdxtpUQYhsn1B0MAsAHM/rHDx+BdzRP6Ik93JBmWbbuqqqI/VPHx+YXR6Bvj8djTDBNvkswQplCku4oC3Zq5U02H4zjwfR9BEIQ803YgdRWGFIosLXUVeH7gmpYb2I4bAGEmCHzfd03LngdZls3JSi+cYmmG5/WGhgfgj205v415cnbOsK8dGZpuYDLVoOkmDNOEbsxNND3cWCIHh0c7l1fX2UZLOL6v1dPVWj0d3tBdtXZSbzSzN+Xy7g+CXByoGF/qcwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="2136"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/32.9d190eb.2384.png" srcset="/assets/ideal-img/32.9d190eb.2384.png 2384w" width="2384" height="2136"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAABVklEQVQYlV3LSU7CYADF8d5FMAqCOGDkLIQNRHcewHgDF0YQy2TQjYjI0DVQxOEIDgiUDUIxYgULHT7o9wxGiHHxW73/Y1YcDu/Gpotfd7oyFqudM5kXONO8hZszL3JLNkfGvrxatNocW8zu3l4kw+VQ4MvI5wso8WXw5XsUS7fgS2Vc39whk83FGZZlD5uvLQDQAIz/0SZDqy0GmcBRMPBSrUHVNCKKHTRbIjpv7+h2PyBJ0kgeKqhUayzDhsKBuiBAVsak1x9A13VQSqdGiqahWhdYJhSOBGqCAJUYZKhoUFV1FgP4E4bC/mpdAAWIoqp0OJQpIYQaBqWGYRBF/Q0j0ai/LjQmb11WxkajrRgApnSNENSExjGzfxBkH54qkAcDSJ99NEUJvf7XjNTr4/G5EmO82ztrkdiJL5VOuxPJpCebTXkSycsf5xdJd+oq7Yufnjm/AZXrPhx3I5zCAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="2346"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/33.df2cc01.2384.png" srcset="/assets/ideal-img/33.df2cc01.2384.png 2384w" width="2384" height="2346"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA0UlEQVQImWPQ19OLMjUz32VsYrFKS0tnrbKy6lplVY21Ssrqa7W09Vbr6hlu19TSzWWorqnuX7N+3f9de/b93759x/89u/b837X34P8du/b937V77/99+w/+X71m3VqG2bNmtj599uz/////v/7///8nGv4Gknj+8tVChp7+qf1Xrt34/+HTl/8vXr/7/+HDh//fvn3///Xbt/8fPn38//b9h//Xbtxcw5CeU2q1aMmy8m07duRt3rqtAIy3bC3YvHVrwZat2/K3bttetnzFSjcAIfCKW1CI7r8AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1192" height="585"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/34.6bcd94f.1192.png" srcset="/assets/ideal-img/34.6bcd94f.1192.png 1192w" width="1192" height="585"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAuElEQVQImXXHywqCQBhA4XnGLDMzCop6l6BF0HO0tW2LQmQCWxS28DYamJbXCWpG/aP2HfgWBynD4XwynR1lebBri31NEGWtJXS1jtjTJFnZj0ZjoyspS2Tb1uZ6SyCKHxCGETySDNI0g6IooSwpUPoE4gdbFMf39Ztx4Lx6McZ40zQcAH6qqnoBAOR5rqIkzdS6rr//N0rpFh2M88Jx3DMhRPcIwbbjYcvxsOt6mPi+7gfByTQvqw83DKZA9HakEAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3432" height="1764"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/35.1203607.3432.png" srcset="/assets/ideal-img/35.1203607.3432.png 3432w" width="3432" height="1764"></noscript></div></div><p>자! 이제 데이터를 열심히 <code>Google Analytics</code> 에 바췄으니 여러 데이터를 가공해서 insight을 도출해줄 것입니다.</p><p>보고서를 어떻게 가공할 지는 여러분들의 손에 달렸습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="ua도-생성하자">UA도 생성하자<a class="hash-link" href="#ua도-생성하자" title="제목으로 바로 가기">​</a></h2><p>원래 <code>GA4</code> 만으로 하려고 했지만 구글 애드센스 연결의 경우 <code>UA</code> 만 지원한다는 것을 알게되었습니다.</p><p>그리고 제가 <code>GA4</code> 에서 <code>Ads</code> 라고 불린게 구글 애드샌스 인 줄 알았는데 이는 구글 애즈 라는 완전 다른 서비스 입니다…</p><p>찾아보니까 아직 <code>GA4</code> 에 지원 계획은 없다고 하고 <code>UA</code> 과 <code>GA4</code> 2가지를 함께 사용하는 것이 절충안 인 거 같습니다.</p><blockquote><p><a href="https://webmasters.stackexchange.com/questions/132548/in-google-analytics-4-how-do-i-link-adsense/132558#132558" target="_blank" rel="noopener noreferrer">관련 질문 내용</a></p></blockquote><p><code>UA</code> 는 2023년 7월 1일 부터 중단 예정이지만 그때까지라도 <code>UA</code> 의 정보와 함께 구글 에드센스 정보를 보는 것이 좋다고 판단하여 <code>UA</code> 도 다시 만들었습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="ua-생성-방법">UA 생성 방법<a class="hash-link" href="#ua-생성-방법" title="제목으로 바로 가기">​</a></h3><p><code>UA</code> 만드는 과정은 <code>GA</code> 와 동일한데 고급 옵션에 <code>UA</code> 생성을 클릭하면 됩니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAVElEQVQImT3FWQ7CMAxAwdz/qqyt7STe+hBC4mM0Y58n9noSqqQpofJX07jWZIsxlhkmwndfk46gIulMqPrpZogoj9ud432gqrg72xOPIqvIhgv4AADIdONlTCG4AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1305" height="415"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/36.3bb4bc5.1305.png" srcset="/assets/ideal-img/36.3bb4bc5.1305.png 1305w" width="1305" height="415"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="search-console-링크-추가-1">Search Console 링크 추가<a class="hash-link" href="#search-console-링크-추가-1" title="제목으로 바로 가기">​</a></h3><div class="admonition admonition-caution alert alert--warning"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Search Console 속성이 GA4 에 연결된 경우 UA 에는 연결하지 않는도록 합니다.</h5></div><div class="admonition-content"><p><code>Search Console</code> 속성이 Google 애널리틱스 <code>UA</code> 및 <code>Google 애널리틱스 4</code> 속성에 모두 연결된 경우 Search Console 인사이트에는 <code>UA</code> 속성의 데이터만 표시하게 되어 <code>UA</code> 는 언젠가 종료될 예정이니 <code>GA4</code> 만 보는 것이 더 합리적 일 것입니다.</p><p>아래의 글은 <code>GA4</code> 와 <code>UA</code> 모두 <code>Search Console</code> 연결 시 어떤 문제가 발생하는지 서술하기 위해 작성되었습니다.</p></div></div><p>생성된 <code>UA</code> 도 Search Console 링크를 추가해주도록 합니다.</p><p><code>UA</code> 에 추가전 <code>GA4</code> 와 비교를 위해 본다면 <code>GA4</code> 만 추가했는 땐 Search Console에서 아래와 같이 표시되고 있었습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA2UlEQVQYlTWPTU/DMBBE8/NySBFUKZQKEBJ/nFNJ06b5cGzH2XUeigOHJ81hdmY2O31+8XL64Kk8UhR78ocD+a4kL/bsHg88v75THt/IzteO6tZTt45zbajunkvruXaBWx9oek91N2RN5+itMnrBOUdUJcaIiKAaiQuIQnZpDN04J6Oq0BuD9x4VSQcskTArWf1nHJxw721iGCcGO2FswLiZwc5bYmtCMjatYXQBHzQxzWtaxE76v1EY/bpNWZaYKpNeq1mYJZJ9/wxUraSHjJOUPNiVbc6mhV99PiNFeeWZLgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1327" height="1062"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/37.b0040c2.1327.png" srcset="/assets/ideal-img/37.b0040c2.1327.png 1327w" width="1327" height="1062"></noscript></div></div><p>UA 에서 Search Console 링크를 추가해봅시다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAmElEQVQYlTWOCwrDQAhE9/5nLU3qXzfJFC1dGFbw8cZ1kOFFBdaCuyMiEJmo2pOsjb03VmbO4i2M8/iAmSAiUHOIxoDXdWFVFWoXThWcJ0NVwCwDm9k0jDG6zgOkBSKCmUJVB+xTWjTGjEBmgN3xIYGqzbItf+i+b6z/4JFgcZjnmPr2BvvvrOd50E9FwcTI/SDyZ2tBp6u/rxcRCqhAbaEAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1517" height="1048"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/38.681cb38.1517.png" srcset="/assets/ideal-img/38.681cb38.1517.png 1517w" width="1517" height="1048"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAM0lEQVQImW3GsQ0AIAwDQfZflwQlOMZIiIKC4vTfhrusmyZw4PZ9oNQyUxkhkqqqL3JpA5BsTqc7C0xNAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1519" height="302"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/39.9d6aaba.1519.png" srcset="/assets/ideal-img/39.9d6aaba.1519.png 1519w" width="1519" height="302"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAYElEQVQImT2NgQrAIAhE+/9/bTgry6xu6MYOHgj3ONPeG6qK1hrEEYl7jBGstYJ0zsGcEzczmBm1VpRSXlkV3v+iR1wiQiOCfms+4AnRzGC+SIQrZxTmeN97RxdB9GZ4AP3Ym/gWoj7JAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="779" height="357"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/40.bbc8b5f.779.png" srcset="/assets/ideal-img/40.bbc8b5f.779.png 779w" width="779" height="357"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAlUlEQVQImX3MvQqCUACG4XNZtbTWEi4RBNVQUUS4dDNNieiS4ims+zINQ/T8vJHU0NIHD3zTKwazDePtHmflMlzscJYu/ema3mhO15nQ+RBhkhLEF/zojB9JvFOCd5Ic4xtenBLKK4FMESiFqSpoGv5NPMuSIruTZxmqrlta6x9KaYS1FmMM2hje/8tY25by4sEhjHgBk86nE/kbc+cAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="593" height="292"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/41.f3ba989.593.png" srcset="/assets/ideal-img/41.f3ba989.593.png 593w" width="593" height="292"></noscript></div></div><p>생성한 <code>UA</code> 를 선택합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAiElEQVQYlX1Pyw7DMAjL//9ktimHJsqTPA9NPBGp62UqEgKBsY3ovYOIsNbCUwgGeu8RY0RrDWOMvylGH9Ba/4ClFPDxlbXWPRO+JvgYtnxKadcLPOe8pWPLeH3ekFJCKQVjDI7jgLUWzjnknDerYDSltH2GEDYbL5iRrXBfWfrpW97M88QgwhdLog+AGCTMvQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="594" height="406"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/42.b7b7381.594.png" srcset="/assets/ideal-img/42.b7b7381.594.png 594w" width="594" height="406"></noscript></div></div><p>아래와 같이 UA 와 GA4 모두 잘 연결된 것을 확인할 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAo0lEQVQYlV2Oi2rEMAwE/f9/2rSXtE6ck/WaYic9jgqWRUIrTVnWk4/HzvDPH2PZnizr6I+X1qqUup8c7aSr0VVv9+nSA7XEIymqipthZmQm7j4VEUTknJFJOVqjtca+H/SudOnUWufyVTlDxe7017rx2L5xj39XL5V2yhujoeZIN0T05apOeYogIgzWwRmDCeaVyTeeJ5QBfDWXj5czcC/+6RfMOjl6R4cz1gAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="931" height="752"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/43.18e4b9e.931.png" srcset="/assets/ideal-img/43.18e4b9e.931.png 931w" width="931" height="752"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="연결하고-보니까-search-console-가-ua--ga4-모두-연결된-경우-ga4-통계를-보기가-곤란하네">연결하고 보니까 <code>Search Console</code> 가 <code>UA</code> , <code>GA4</code> 모두 연결된 경우 <code>GA4</code> 통계를 보기가 곤란하네?<a class="hash-link" href="#연결하고-보니까-search-console-가-ua--ga4-모두-연결된-경우-ga4-통계를-보기가-곤란하네" title="제목으로 바로 가기">​</a></h3><p><code>Search Console</code> 가 <code>UA</code> , <code>GA4</code> 모두 연결하고 보니까 상단에 위와 같은 안내 메시지가 나오고 있었습니다.</p><blockquote><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAALUlEQVQImWN4+e7b/5dvP/9/9e7b/9cff/9//f77/1fvQPwvUPz1/7tPv/4DAAozJg3B/XjlAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="929" height="92"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/44.2863904.929.png" srcset="/assets/ideal-img/44.2863904.929.png 929w" width="929" height="92"></noscript></div></div><p><a href="https://support.google.com/webmasters/answer/9419894?hl=ko#analytics&amp;zippy=%2Cgoogle-%EC%95%A0%EB%84%90%EB%A6%AC%ED%8B%B1%EC%8A%A4-%EC%97%B0%EA%B2%B0-%EC%9A%94%EC%B2%AD" target="_blank" rel="noopener noreferrer">Google 애널리틱스 연결 요청</a></p></blockquote><p>Search Console 속성이 Google 애널리틱스 <code>UA</code> 및 <code>Google 애널리틱스 4</code> 속성에 모두 연결된 경우 Search Console 인사이트에는 <code>UA</code> 속성의 데이터만 표시된다 라는 건데 전 <code>GA4</code> 정보를 우선해서 보고 싶음으로 <code>GA4</code> 만 연결하도록 변경하였습니다.</p><blockquote><p>어짜피 <code>GA4</code> 만 연결해도 Search Console 정보는 볼 수 있으니까 문제가 없고 <code>UA</code> 는 언젠가 종료될 예정이니 <code>GA4</code> 만 보는 것이 합리적 이라고 생각했습니다.</p></blockquote><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAQ0lEQVQImQXBARKAIAwEMf7/VB3tYBEoV1mTMmcQEVy3Ud2RxByDvTcp0d0xM0qsJPPD20v1hpQsJRGL3gfncVIf5wd1KE2u8ejXDAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="934" height="181"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/45.2d62604.934.png" srcset="/assets/ideal-img/45.2d62604.934.png 934w" width="934" height="181"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="애드센스-링크-추가">애드센스 링크 추가<a class="hash-link" href="#애드센스-링크-추가" title="제목으로 바로 가기">​</a></h3><p><code>UA</code> 를 추가한 이유죠 애드센스 링크를 추가하도록 합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAASklEQVQImU3KUQ6AIAwEUe5/VxSUQluUMUaibvK+ZsOyVeLu1CqUIkhVzPzhkzmhqWLeSVnIa5ono3dnjPEKdziPTkyNpg588b8LGTt1hokjjdsAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1491" height="457"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/46.5f1af8f.1491.png" srcset="/assets/ideal-img/46.5f1af8f.1491.png 1491w" width="1491" height="457"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA30lEQVQYlV2QCW7DMAwE9f9HtnDjI5aj07YOX1NIaYO0BAYgwOVyQeG9wxiDdw4/z6zryt+6KsI8NEM/0LUtXd8jR4mUkr7rud9HpHLceoVo3RetvGG0QWuNtbbinSfFxBJP3HogxkUxqomUEvu+k1N6Cr1n27ZXAPHxyAxqJoaF8yp54DiOF2X5OE9EGWitaJqmUvLFGMk5V0pfnKuwnJHThLGWNQSu66pu7/UUPjT9Z4MeBlTbsjhHyrku/CK2fUNNGj0ZFutYrSWGULMVwXmeFRFCYF7mf0/+efWb4zcAOoK1Lj6cjQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="395" height="375"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/47.74f880f.395.png" srcset="/assets/ideal-img/47.74f880f.395.png 395w" width="395" height="375"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAnUlEQVQImU2PQQqDMBREc/8D9RyFrkupC7U2GmPMT7RJXlGqdGBgFvNgRuleo7XGmBE7jtjJY50nxkgI4bSq+orZzUzThLWWZU18UoFSKD9DQTkxiPc7lXPm0H8p5YJ61DPGOmIMeC/42bMsywls2nh1uX541gPdq6FpWtq2pes6hmHAGINzjhAEdbsLzVv2AyKyT9iOHHldV1JKfAGIxOfJWrhEdwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="730" height="470"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/48.f56e87e.730.png" srcset="/assets/ideal-img/48.f56e87e.730.png 730w" width="730" height="470"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>이와 같은 제품에 분석 도구를 도입하는 것은 중요한 작업입니다.</p><p>서비스를 관리하고 운영한다는 것은 끈기와 세심한 노력도 중요하지만 현재 나의 서비스가 잘 운영되고 <code>성장</code> 하고 있는가와 같은 <code>지표</code> 정보는 중요합니다.</p><p>이는 현재 나의 상황을 알 수 도 있으며 앞으로의 <code>성장 동력</code> 으로써도 사용됩니다.</p><p><code>React</code> 만 배워도 <code>웹앱</code> , <code>웹 사이트</code> 와 더불어 일렉트론을 이용해 <code>Client Program</code> 을 손쉽게 만들 수 있는 세상이고 <code>Flutter</code> 만 배워도 <code>Android</code> , <code>iOS</code> 앱을 손쉽게 만들고 배포할 수 있는 세상입니다.</p><p>즉, 개인 서비스를 와 같은 <code>Application</code> 을 손쉽게 만들 수 있는 세상인데 이러한 분석 도구를 잘 배워놓으면 서비스 론칭에 대한 자신감을 가질 수 있습니다.</p><blockquote><p>필자도 <code>Google Analytics</code> 를 살펴보면서 네이티브 앱을 공부하고 싶은 생각이 많아졌습니다. 어떻게 로그가 쌓일까? 이런 호기심으로 말이죠 🤩</p></blockquote><p>깃허브 블로그에 처음으로 Google Analytics 도입할 때 막막했던 심정으로 작성된 글인데 누군가 한태 도움이 되었으면 좋겠네요. 읽어주셔서 감사합니다.</p>]]></content>
        <category label="blog" term="blog"/>
        <category label="Google Analytics" term="Google Analytics"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[동일 페이지, 다중 URL의 SEO 처리 방식]]></title>
        <id>/2022/10/23/same-page-multiple-urls-seo</id>
        <link href="https://parkgang.github.io/blog/2022/10/23/same-page-multiple-urls-seo"/>
        <updated>2022-10-23T11:00:00.000Z</updated>
        <summary type="html"><![CDATA[새롭게 블로그를 개발하거나 리펙토링 하면서 글의 URL 이 변경될 수 있습니다. 동일한 글인데 URL 이 2개 생기는 경우 우리는 어떤 URL 에 대해서 가중치를 주고 Index 에 등록을 해야 할까요? 2개의 URL 모두 Index 에 등록하면 화력이 분산 되지 않을까요? 제가 직접 새롭게 블로그를 개발하면서 겪은 이 문제에 대한 결과를 공유합니다.]]></summary>
        <content type="html"><![CDATA[<p>새롭게 블로그를 개발하거나 리펙토링 하면서 글의 <code>URL</code> 이 변경될 수 있습니다. 동일한 글인데 <code>URL</code> 이 2개 생기는 경우 우리는 어떤 <code>URL</code> 에 대해서 가중치를 주고 <code>Index</code> 에 등록을 해야 할까요? 2개의 <code>URL</code> 모두 <code>Index</code> 에 등록하면 화력이 <code>분산</code> 되지 않을까요? 제가 직접 새롭게 블로그를 개발하면서 겪은 이 문제에 대한 결과를 공유합니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="상황">상황<a class="hash-link" href="#상황" title="제목으로 바로 가기">​</a></h2><p>작성된 블로그 글에 <code>Index</code> 를 등록하고 구글 상위에 노출되면서 사용자가 잘 찾아오고 있는데 블로그가 변경되면서 해당 글의 <code>URL</code> 이 변경되는 경우 <code>Index</code> 처리는 어떻게 될까요?</p><p>우리는 이 상황에서 아래 중 어떤 것을 택할지 고민하게 됩니다.</p><ul><li>이전 URL을 그대로 유지</li><li>새로운 페이지의 URL을 새롭게 Index에 등록</li></ul><p>혹여나 구글 상위에 잘 노출되고 있는 글이 새로운 <code>Index</code> 등록으로 문제가 되지 않을까 노심초사를 하며 말이죠…</p><p>하지만 검색 엔진은 그렇게 멍청하지 않았습니다. 어떻게 처리되는지 설명해 보도록 하겠습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="시나리오">시나리오<a class="hash-link" href="#시나리오" title="제목으로 바로 가기">​</a></h2><p>각 <code>시나리오</code> 는 <code>동일한 글</code> 에 대해서 <code>변경 전 URL</code> 과 <code>변경된 URL</code> 이 어떻게 <code>Google Search Console</code> 에서 처리되는가에 대해서 설명할 것입니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="전제-조건">전제 조건<a class="hash-link" href="#전제-조건" title="제목으로 바로 가기">​</a></h3><p>기본적으로 아래의 전제가 깔려있습니다.</p><ul><li><code>변경 전 URL</code> 클릭 시 <code>변경된 URL</code> 으로 <code>리디렉션</code> 처리됨</li><li><code>변경 전 URL</code> 의 페이지는 <code>변경된 URL</code> 으로 <code>rel="canonical"</code> 가 적용되어 있음<br>참고: <a href="https://developers.google.com/search/docs/advanced/crawling/consolidate-duplicate-urls?hl=ko" target="_blank" rel="noopener noreferrer">중복 URL을 표준 태그로 통합 | Google 검색 센터 | 문서 | Google Developers</a></li><li><code>URL 변경 후</code> 따로 <code>Google Search Console</code> 에서 건드린 것이 없음</li></ul><p>아래는 <code>변경 전 URL</code> 클릭 시 서빙되는 파일인데 코드로 보면 아래와 같은 것입니다.</p><div class="language-html codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-html codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token doctype punctuation" style="color:#393A34;font-style:italic">&lt;!</span><span class="token doctype doctype-tag" style="color:#999988;font-style:italic">DOCTYPE</span><span class="token doctype" style="color:#999988;font-style:italic"> </span><span class="token doctype name" style="color:#999988;font-style:italic">html</span><span class="token doctype punctuation" style="color:#393A34;font-style:italic">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">html</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">meta</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">charset</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">UTF-8</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">meta</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">http-equiv</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">refresh</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">content</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">0; url=/blog/2021/05/06/using-recoil-in-nextjs/</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- 변경된 URL으로 적용 --&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">link</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">rel</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">canonical</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">href</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">/blog/2021/05/06/using-recoil-in-nextjs/</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">    </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 변경된 URL으로 리디렉션 처리</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">    </span><span class="token script language-javascript dom variable" style="color:#36acaa">window</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">location</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">href</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">"/blog/2021/05/06/using-recoil-in-nextjs/"</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">html</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="변경-전-url-이-변경된-url-보다-인기가-더-많은-경우"><code>변경 전 URL</code> 이 <code>변경된 URL</code> 보다 인기가 더 많은 경우<a class="hash-link" href="#변경-전-url-이-변경된-url-보다-인기가-더-많은-경우" title="제목으로 바로 가기">​</a></h3><p><code>변경 전 URL</code> 인 <a href="https://parkgang.github.io/golang/lets-create-an-http-cache-server-with-golang/" target="_blank" rel="noopener noreferrer">https://parkgang.github.io/golang/lets-create-an-http-cache-server-with-golang/</a> 이며 해당 링크를 검사해 보았습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAQCAYAAAAvf+5AAAAACXBIWXMAAAsTAAALEwEAmpwYAAABcklEQVQokT2RS7LaMBBFtf+1ZJJJ9pCqTDJIJgmPAMZgMLL+H8v2SUnAU9Up6bZut1qS6Hd7Tn8+6P7u6XZ7+o8D/f7JefePw+7AoR8Rw/BgkgZnI9YErIk4m5qOYeb7j598+foN0V+P9ENP353pz0+iD+SYSDGylkIdIjtLiAGtdduIIbCUQk6pEWMkpYQISuN0xZBDIPtAsBavDVYpnNJtLWytZg3zXNiAlDPOe3wIGGtxzrMsKyKljLWWeZ7bkfXo2kbVpZRGXYuYElLKZvbeM00Tl8vlMznn3BAxplahNv2mJlRjNbzNIqbMbRhapRo0xnC9XpvOr2qfRimn1nhZFrwPzKWwbhvLsrRY1aI1P97xaqK9qVZNq/utYR4jUWtEtA7VHVHnE+lxb/Px9y9u+11j6k6E4YIIISLHES0lZppIzpGDZ46hUT8heYeQymFDxoYZ45/zm6ZfMaGcZZSyXWLbIOcZpTRaG9Z1e11q5T8yHWVd6LQ01gAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="734" height="1165"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.ffd4098.734.png" srcset="/assets/ideal-img/1.ffd4098.734.png 734w" width="734" height="1165"></noscript></div></div><p><code>변경 전 URL</code> 은 <code>Index</code> 가 살아있는 상태이네요.</p><p>보니까 <code>색인생성</code> 이라는 부분에 새롭게 정보가 추가되었는데 <code>리디렉션</code> 과 <code>canonical</code> 를 명시했기 때문에 <code>검색엔진</code> 이 다시 <code>크롤링</code> 한 결과 <code>표준 페이지</code> 는 <code>변경된 URL</code> 인 <a href="https://parkgang.github.io/blog/2021/11/17/lets-create-an-http-cache-server-with-golang/" target="_blank" rel="noopener noreferrer">https://parkgang.github.io/blog/2021/11/17/lets-create-an-http-cache-server-with-golang/</a> 라는 것을 이미 알고 있네요!</p><p>이를 통해서 <code>변경 전 URL</code> 이 어떤 <code>URL</code> 으로 알 수 있는 정보는 <code>사용자 선언 표준 URL</code> 이라고 하는 <code>rel="canonical"</code> 곳에 선언할 수 있다는 것을 알 수 있었습니다.</p><p><a href="https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls?hl=ko" target="_blank" rel="noopener noreferrer">공식 문서</a>를 읽어보면 알 수 있지만 <code>표준 페이지</code> 를 명시하더라도 검색 엔진이 보기에 더 적합한 페이지를 <code>표준 페이지</code> 라고 지정할 수 있다고 합니다.</p><p>즉, 우리는 검색 엔진에게 <code>표준 페이지</code> 힌트를 제공할 뿐 변경은 <code>검색 엔진</code> 마음입니다.</p><p>예를 들어 <code>변경 전 URL</code> 이 웹상으로 더 공유가 많이 되었다면 <code>변경 전 URL</code> 클릭 시 <code>리디렉션</code> 되어 <code>변경된 URL</code> 으로 접근이 될지언정 검색 엔진은 <code>변경 전 URL</code> 이 더 <code>표준 페이지</code> 와 적합하다고 생각하는 것입니다.</p><p>그래서 <code>표준 페이지</code> 로 지정한 <code>변경된 URL</code> 을 검사하면 아래와 같이 <code>Index</code> 가 생성되지 않은 것을 볼 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAPCAYAAADd/14OAAAACXBIWXMAAAsTAAALEwEAmpwYAAABOklEQVQokXVQy0oEMRDM//+MR0/+gCdPoiCCyz4y60wmk1d3kpkt6dZdRDBQ9KuorrQ5Dg774ycOdlKcBofT2UH69tPj5e0Dr+97mGH08JGQaEUsHbE0jVJTAx6fnnF3/wAzTg7WDjgcj4qTtZhnj9Y6iBnbtkGeab2jVkYpGUQFrTUdMjOYCKUUzU3hhkIM4obWV3DtKFSRMiFn0jwTw6SUsSwLmOu3UhVSRkwJRITtckFfVxjxEUJArVXX5pwxTRO89wjLorX0jax1zimx966DcRwRY1RF8adEURSFq6pEa60O2w+kb4irKojPdV0xzzN2u93NyjUaom/FlNJttZB/E38UGbNzal78iLfzMNyIV+hnZr9gCREhJiXLFj34LxjnE0JihMyIuf4LI9f3qhgQYsS6bXrkv/gCYDhCiID2W8YAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="734" height="1089"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.b6fe6b1.734.png" srcset="/assets/ideal-img/2.b6fe6b1.734.png 734w" width="734" height="1089"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="변경-전-url-이-인기가-많지만-변경된-url-으로-바꾸고-싶다면"><code>변경 전 URL</code> 이 인기가 많지만 <code>변경된 URL</code> 으로 바꾸고 싶다면?<a class="hash-link" href="#변경-전-url-이-인기가-많지만-변경된-url-으로-바꾸고-싶다면" title="제목으로 바로 가기">​</a></h3><p>그렇다면 <code>리디렉션</code> 과 <code>canonical</code> 를 명시해서 내가 지정한 <code>표준 페이지</code> 를 지정되면 어떻게 표시될까요?</p><p><code>변경 전 URL</code> 에 대해서 조회해 보겠습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAQCAYAAAAvf+5AAAAACXBIWXMAAAsTAAALEwEAmpwYAAABX0lEQVQokWWSy4ocMQxF/f+/lGU2IbvZJIRA0qF7uqpcDz8l2S4PN8iTaRpiOEi2rnUNspkuV8zXO9b7MtjuFvtkEXJF4hM/ft3w9eU7jN0DXCCEXOCTDCJVkJzg0vHy7Sc+ff4CcziHdbWYpgnzPMNaixA8WmsopeBjmd7fxqGIjMJ5nqNQa0URAbOARWAyF8REYKlo5xuktLFXNFe0ZogYMUbU2tB7H92JaDj03h+YUiq898NWRXppWZYhbK2NJyhGpOA4DmQi5JwRQsC+7yNv/4QaH0K100ON27YNl/bcUa2fC2qt+Yd1e+74sDpPMPOIz93e30iEsMzI+4riHbbbFfPlN/bXG9w84ZjuiMsMIzkjq9DOKMeGtExY/1zgXq+g1aL5A3XfYIQF3jmkGEEpjWkIEzhnCDOqTowZZvcZRyC4yINE+muU9h51TxVGR+V8gA8RISbUeqK2/h9/AagSZ1uj8B5bAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="733" height="1183"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.f55e45f.733.png" srcset="/assets/ideal-img/3.f55e45f.733.png 733w" width="733" height="1183"></noscript></div></div><p><code>변경 전 URL</code> 인 <a href="https://parkgang.github.io/next.js/using-recoil-in-next.js/" target="_blank" rel="noopener noreferrer">https://parkgang.github.io/next.js/using-recoil-in-next.js/</a> 는 이전에 <code>Index</code> 가 생성되어 잘 운영되던 페이지입니다.</p><p>근데 이제는 <code>Index</code> 가 없다고 하네요? 이유는 <code>사용자 선언 표준 URL</code> 을 보면 제가 <code>리디렉션</code> 과 <code>canonical(케노티컬) 태그</code> 를 <code>변경된 URL</code> 으로 <code>Index</code> 가 생성되었다고 합니다.</p><p>그럼 <code>변경된 URL</code> 인 <a href="https://parkgang.github.io/blog/2021/05/06/using-recoil-in-nextjs/" target="_blank" rel="noopener noreferrer">https://parkgang.github.io/blog/2021/05/06/using-recoil-in-nextjs/</a> 으로 검사를 해볼까요?</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAPCAYAAADd/14OAAAACXBIWXMAAAsTAAALEwEAmpwYAAABZ0lEQVQokT2RyWodQQxF6/8/KWSTfRYm4GCyCcGvXw/pGlVz9/MJXbEtuOhKSLoSUvOfieV15u99/cQ2rezzhuSDX78nvv94Qa3zitGW4ARnPN4KSQpJMjkWnp6e+fL1G2rdbqz7ynK/sy4LyzxTSqG1SquVD1NnztTWkRhpvdNa4/H2Rj+OwUup5FJRYi1Wa8R5ei4UiYh1uF0TnSdLJIWAkpwIEuitc57nkMs5ky6Fa/rjwXkcqFob3vuR7L0TY2TbNqy1OOdGfOXVpW+M/ixMKWGMGc39fedRWGpF6310X0kRGROv+KPwgiq1oY0hiHCcJzGlcf11+RUPXDuWXDDbijhLz4kcAsFovNGINYMn71BFBD+9Eu436r4Nfnv5OXxc7sg8keYJdX3B6Z1gDdE7ahRKFI6S6fk/Wk4o4yLGR2zIOCmEVPGx4N95iHV4ZUJAW0uMmeN4jHdZ63E+0PtJP05aO/gHlA4/RcrUipIAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="734" height="1114"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.1697954.734.png" srcset="/assets/ideal-img/4.1697954.734.png 734w" width="734" height="1114"></noscript></div></div><p>검사를 해보니 <code>Index</code> 가 등록되었다고 합니다.</p><p>중요한 것은 저는 <code>변경된 URL</code> 으로 <code>Index</code> 를 요청한 적이 없습니다. 하지만 그럼에도 자동으로 생성된 것을 볼 수 있습니다.</p><p>그리고 하단에 <code>색인생성</code> 부분을 보면 <a href="https://parkgang.github.io/golang/lets-create-an-http-cache-server-with-golang/" target="_blank" rel="noopener noreferrer">https://parkgang.github.io/golang/lets-create-an-http-cache-server-with-golang/</a> 와 다르게 <code>Google에서 선택한 표준 URL</code> 이 <code>검사된 URL</code> 과 같다고 합니다.</p><p>즉, 제가 <code>리디렉션</code> 및 <code>케노티컬 태그</code> 로 원하는 <code>표준 페이지</code> 로 지정한 페이지를 <code>검색 엔진</code> 이 선택되었다는 것을 의미합니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="index-는-있지만-인기가-없어서-갱신이-안된-경우"><code>Index</code> 는 있지만 인기가 없어서 갱신이 안된 경우<a class="hash-link" href="#index-는-있지만-인기가-없어서-갱신이-안된-경우" title="제목으로 바로 가기">​</a></h3><p><code>트래픽</code> 이 없어서 <code>검색엔진</code> 이 아직 <code>크롤링</code> 을 시작도 안한 경우는 <code>변경 전 URL</code> 과 <code>변경된 URL</code> 중 어떤 것을 <code>Index</code> 으로 생성해야 될까요?</p><p>우선 <code>변경 전 URL</code> 으로 검사를 해봅시다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAQCAYAAAAvf+5AAAAACXBIWXMAAAsTAAALEwEAmpwYAAABeUlEQVQokTWRS24cMQxEdf+TZJtVLpBdVtk7mMBx3NM97dGXlMTu8QskOwKIgsRPFUvu79OFP08XXn795vXyPHG5PJNKJ+vBds+8bh633QIhKkWNXDpFjKIHSQxpD77/+MmXr99wi99ZbivLsrJcr1zXldoavRutd87zwTiumyEqxBipqkgpnOeBWcd6p/eGmeFqG5RKFqXbOaO2IUHJRWauSMXlMS2l2fX+Dm0UlYKIklKmiEx6NxM5T5rDDNVREGlT5wf1QDeEj4nNjON8kHJmu92IKc/3EAIigquihHWhBo/liPo7cVtnWK0cx1jMcK1W9tcX/LpgOZH327wPtFanTZ/Unbv3U/TxeCC1Yuc5ZQzrpkXTHhHergvxbaeXQgme4u+fHn5MG+iaCGFbSW87GgMaPHnfZuHQNo23jktZCHFsWCaKNrT2icPo/+F8KsSsiHaaPdB2kEolS6P1c0btJy4UYb8HYhLGd47uu0/4kNFqaDOkdv4B3zdn4DmLeBkAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="736" height="1136"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.51184f8.736.png" srcset="/assets/ideal-img/5.51184f8.736.png 736w" width="736" height="1136"></noscript></div></div><p><code>사용자 선언 표준 URL</code> 도 지정되어 있지 않고 캡처한 시점이 <code>2022-07-09</code> 인데 <code>크롤링</code> 날짜는 <code>2022-05-01</code> 으로 낙후된 것을 볼 수 있습니다.</p><p>그럼 혹시나 하는 마음으로 <code>변경된 URL</code> 도 한번 조사해 볼까요?</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAPCAYAAADd/14OAAAACXBIWXMAAAsTAAALEwEAmpwYAAABMUlEQVQokYVRPW+FMBDL//8vXTt179Spc5eKhhcgAfKdkODqruXpdSqSxcVYti8IqTSGcYa8aXzdNEZlIJXBOK2woeJLGXx8jhBq2bC5DB8POEblOeTGeH17x9PzC8SyaNxuClKOkFLyvK4baj2Qc8F5nqBHHAcRGTEGpBRRa0XvnbmcE2JKyKVApFwQY0bKFfVoyOVATAU+JAbNIWUI7wP2fUcp5e7kQ4D3Hikl9H7iaA2CPlhrWUixIQQYY7BtGxvQmXhBHYzRfKC+5DTPM5xz7EhGLEw5Q2vNrkSQi1KK5/oLSuPoZVlY0Frj2GEY7lWut0jpx5Eir+h1Xf8I2ZGEV3kiqMI0TXfhBV5mpQ2tg3Oey19LPEKYzcOF8i+ED5H/7W4trHNovaOfJ1/0I74BCaVCzcbX96oAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="732" height="1090"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.d0c3c24.732.png" srcset="/assets/ideal-img/6.d0c3c24.732.png 732w" width="732" height="1090"></noscript></div></div><p>역시 등록되어 있지 않네요.</p><p>이럴 땐 어떻게 하는 것이 현명할까요? 그냥 <code>변경된 URL</code> 으로 <code>Index</code> 를 생성하는 것이 좋을까요? 아니면 <code>변경 전 URL</code> 으로 다시 <code>Index</code> 하여 <code>검색 엔진</code> 이 알아서 선택할 수 있도록 하는 것이 좋을까요?</p><p>사실 지금 말로 정리하면서 정답이 나왔죠? <code>검색 엔진</code> 이 알아서 선택할 수 있도록 하는 것이 좋습니다.</p><p>우리 입장에서야 <code>변경된 URL</code> 이 더 <code>Fresh</code> 하고 좋다고 느껴지지만 실제 <code>Index</code> 시 어떤 페이지가 더 가산점을 받을지는 <code>검색 엔진</code> 만이 알 수 있습니다.</p><p><code>검색 엔진</code> 이 선택한 <code>URL</code> 이 어떤 것인지 어떻게 알 수 있을까요? <code>URL</code> 검사를 통해 알 수 있습니다.</p><ol><li><p><code>변경 전 URL</code> 에서 <code>실제 URL 테스트</code> 를 눌러보도록 합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAPCAYAAADd/14OAAAACXBIWXMAAAsTAAALEwEAmpwYAAABOElEQVQokU1RW27DMAzz/a+17qPfw4ZhF2iTOo1tWZYfTThIfWwGCMIyRQqWO88Bk4/wa8YS2FgRuYO44/PjG4fDEe7kr1gCIaSCNTICCUq9gWUYf3794O39CDdHj9nPOJ9OmKYzpmlCrYLeGlpreB63bTukVlBm9D7Qx8C2bai1odaKIgIuBY5KQaSMIhV9bJDarEkRIyFRtppjKaBMGGNg33eLE5G7WynGmuA0IoZghd47iAiXywU5ZxMys7ETqfaoInVVQQjBmIgML+GyLMiZLVYd9L6u66tZ2Wmk995ctJBSQozR5tS7Niv+HJmtW2NU8BQ92YTX62IuWkiPuUzwaLBo/WyN1UidTyML3+f9DxciI5J+ekFIjFwaWLoxsTxQ4VbKCEk30zBuus6OSLqZYptStH7DL7JsQynGjeG9AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="731" height="1132"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.8674bf7.731.png" srcset="/assets/ideal-img/7.8674bf7.731.png 731w" width="731" height="1132"></noscript></div></div></li><li><p>요청 시점으로 실제 크롤링 된 예상 결과를 보여주는데 여기서 핵심은 <code>사용자 선언 표준 URL</code> 을 인식한 것이고 <code>검색 엔진</code> 입장에서 선택한 <code>표준 URL</code> 은 <code>색인 생성 이후</code> 에 확인된다는 것입니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAQCAYAAAAvf+5AAAAACXBIWXMAAAsTAAALEwEAmpwYAAABcUlEQVQokT2S2XLbMAxF9f8/1+lbpw9t4tiKbFkUV4CLlNMhPc3DmTtccAEQnD7vhmW1rMazmjDYbMSngg0ZF8tg2mwiSEPK1yDlk6jnONxsYnk6Pj6fTDfzYF4W5nnhept5rE9EM6W0oSJKLpVJSybEwL7vhBCotQyyKqpCzpnjOJhKPdBcSVqo7aS1c6jm8nLUQpLMZKPHuh1JaUSqKtbueO85jjb2hmOWwL7+xW3vuO0y1D7fqfKg6UqTOzUHJtU8aksiiAi1Vlprg1LrN1Pvylo7LnV66hgjKaUR1Bsrpbwce8e9pn7QdVmWEVy7W/l/MRee2zbSt+MgdqfWOM9zrDt9PakI2+NO7A4qiPc4Ywj9XXum3ZC8Y1LvcJc33PWCPhbs5Y0/P3+w/P5FmK/42wdpvr4crdlIwZMlkVNCU0RjpKpSs1JE+qeIGBdxQQmp4FPGx/zSbwqT8Y6t1xNljK6Py+wO6zx9vLUe44P8A/KEaGbg3p5qAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="730" height="1184"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.53f6da1.730.png" srcset="/assets/ideal-img/8.53f6da1.730.png 730w" width="730" height="1184"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAcklEQVQImR2NyQrAIAxE/f8v7KEWRJS6b9QFD1PMITOEPF5YKQUxRrTWsPfG2YUQUEpBaw0pJd1YCAG1VowxCEwpgXOO+77xPA+u6yIRO5FzJnitRX1M1lo45+C9Jwk7hgOe6b3j+z4YY+j1+74EzjnxA023lvfwHYkhAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="510" height="229"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9.b32d3d8.510.png" srcset="/assets/ideal-img/9.b32d3d8.510.png 510w" width="510" height="229"></noscript></div></div><p><a href="https://support.google.com/webmasters/answer/9012289#google-selected-canonical" target="_blank" rel="noopener noreferrer">UI의 자세히 알아보기 링크</a></p></li></ol><p>그럼 결과가 어떻게 되는지는 실제 <code>Index</code> 생성이 필요하므로 시간이 걸리겠군요 <code>Index</code> 생성을 요청 후 <code>검색 엔진</code> 이 어떤 것을 <code>표준 URL</code> 으로 선택하는지 볼까요?</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAQCAYAAAAvf+5AAAAACXBIWXMAAAsTAAALEwEAmpwYAAABU0lEQVQokW2SSZIcMQhFdf9TutrhzlZVpuYBkNLfAeX2yosXCAQfNLjjGfD1inheBa9QzZ6xoXRBqoTc2HBX6qhjYfBvdFJutHnb5pU6/Jnx8zjhjnji03sch8evzwP++cKYBOJltg/CJIbrNFBbQYwBtRQIM0QYRBM0J2h03MJwIhuTxCplbax9Q9ZtKmMQekyo5wmXW0XMCb137H1jTkJKGaUUrL2x79uKHc2GdP5Avj5QwofZdD2wxhNrvrCmh1CFU4VaC0ZvBjNhCRvM9BeGG3NaK7VjDEwitN5tFBExWOStGGO0mTRYa4X3X0gpvZNYlRlOT3ee53v4tdBas4S9t/mK+qYYQviXoO21SJUVXWvMEr83tEXOGY/HA957m1MFxphwRHpvyQK6VlRB+T6MtQ6pQcl1onZGafRfXCgFV4xobdgT6kcIMSPlApZtMZaFP3xFad/ZQ7tJAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="732" height="1188"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/10.ba0aeb9.732.png" srcset="/assets/ideal-img/10.ba0aeb9.732.png 732w" width="732" height="1188"></noscript></div></div><p>시간이 지나 결과는 예상하지 못한 <code>리디렉션</code> 오류가 발생했습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAPCAYAAADd/14OAAAACXBIWXMAAAsTAAALEwEAmpwYAAABQklEQVQokYWRzUosMRBG8/6PITroiH+jLepKRHChC1FQ7zC6VNrp1rE76aTSSR9JRu69C8UPDkUqX6pSiXqtGxL1wlC9az60pzGeetFRvzc8Pbdc/tGo6q3hpazRnadzIcfEojEsmg5jBeccKoaI6wy/SR2fnjPeK9gqjhnvHbK+e8D6bsFoe5/x5JD76YzL61vUydkFo50DNiZHrG0XrG7tZ1Y2J4x2Cm4fZlzd3KEI8de2SUrEE0L4kQEIA6iQKg4DwzckBaNx1RzVp1Mx/mNYxhhCNurHKe1sirLSI/4bUr6POG0Q51Gmc/Qh5rvE4T/ikPEh0llZDiPi8F6+8HiRv63TXv6ZVhvKsmQ+n1NVVaYsX7HWZqMxBmsdyjqhbRu01jmZYqoQ4/J9RYTUVTnpSWbnlgnT2bzu+4D7MqXhPgEXJTtpVCA4XQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="742" height="1133"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/11.000e56d.742.png" srcset="/assets/ideal-img/11.000e56d.742.png 742w" width="742" height="1133"></noscript></div></div><p>이전에 색인되어 있었다는 내용은 어디로 가고 이제 색인이 등록되어 있지 않다고 합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAPCAYAAADd/14OAAAACXBIWXMAAAsTAAALEwEAmpwYAAABPUlEQVQokW2Ru1LkMBRE9f9/Q0i0MRERRUSwFGNky2PrffWye+veZWACVNXVLql12pLUNG+46BXs2lh8zhvm1SHkLvp7MXh9e4eazQ4bCCE3+NS+3cWCSANPzy94ePwDFacPrLOGXhZoPWNZFtG6rvAh4DZU7wNjDLTeMcaB4zhwnCdKKUgpI2VCJoJKzmPfLGImtD6QcoHzEZkqqFTxTAWq5IwUI7oQB1prQmPyyTq5ZUBRKbDWyiKH+XuaJnHnnHhKCYqIEEKQENN4MsYoTkQihiii8v+E3qO2hpgSjDHY91023iTVHHTeo9WKTWssxiDnLIFaq0iI1+tVqnprCN4Ljet+gkwkwrZt8tM8yWS+8PtaqebL3K2D9wEhRiHdDnAvtdko7+q/FFL9VSqmDOscfIhCHMcpT3iw3+kf+uRCugr57iwAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="729" height="1087"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/12.5d45a14.729.png" srcset="/assets/ideal-img/12.5d45a14.729.png 729w" width="729" height="1087"></noscript></div></div><p>제 생각에는 다시 <code>크롤링</code> 하니까 이전에 색인된 내용이 필요 없다고 판단하여 제거한 것 같으며 제거된 상태에서 다시 색인을 생성하려고 하니 <code>리디렉션</code> 되는 링크들이므로 잘못된 색인이라고 오류가 발생하는 거 같습니다.</p><p>그래서 <code>변경된 URL</code> 으로 <code>Index</code> 를 다시 생성했습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAPCAYAAADd/14OAAAACXBIWXMAAAsTAAALEwEAmpwYAAABLklEQVQokWWRzU7EMAyE8/7Pw40TD8AFcUBc9kK73S1tmh/H+Wk7yGYLWm2kUZPpl7GtmGFccL5aXCf/oJAaTl8XvH+eYL5twBIYxCtCqghUEVMD5RVcdry+feDp+QVmmi2G4YK+P6PrewzDAGstcilgZuz7DlmmrRtaa2rmnHUva11X3ZdSVCZShvcRlBi1beBc4QPBLh7OB1DKCDHBECV47/WWpMg3pQQi0irbtqlvOBc457RsrVWBcRwxz7P6MUb1TeKs5tGfgHKWVAGkgoKSOE2TTiqmpHRdpz/rDdRhBJQE6VN6OcAj6R/U0pOCR49y8RHUYZa/gaR5GabWX/CQiZSwOA8fokoq5FyQmO9krCP4yAiUVcT1pnYnE4gxW6cvsbigL1PqilLbnX4Av1RC8Me3JfEAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="737" height="1100"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/13.ac760db.737.png" srcset="/assets/ideal-img/13.ac760db.737.png 737w" width="737" height="1100"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="결론">결론<a class="hash-link" href="#결론" title="제목으로 바로 가기">​</a></h2><p><code>검색 엔진</code> 은 똑똑하며 필요한 정보를 잘 적어주면 <code>가중치</code> 에 맞게 필요한 페이지 <code>1개</code> 만 <code>Index</code> 으로 생성되는 것을 볼 수 있었습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마치며">마치며<a class="hash-link" href="#마치며" title="제목으로 바로 가기">​</a></h2><p>제 개인적으로도 <code>블로그</code> 를 개선하면 <code>URL</code> 변경에 따른 <code>Side Effect</code> 가 걱정되어서 찾아보게 된 것인데 <code>SEO</code> 에 대해서 조금 더 이해할 수 있는 계기가 되었습니다.</p><p>이 글을 보고 <code>블로그</code> 를 개선 및 마이그레이션 하면서 <code>URL</code> 이 변경되면 이전 <code>Index</code> 는 어떻게 되는 거지! 라는 걱정 말고 <code>자신감</code> 을 가지고 할 수 있도록 내용이 전달되었으면 좋겠습니다.</p><p>읽어주셔서 감사합니다!</p>]]></content>
        <category label="blog" term="blog"/>
        <category label="Google Search Console" term="Google Search Console"/>
        <category label="SEO" term="SEO"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Private Repo에서 GitHub Blog 만들기: GitHub Actions 활용]]></title>
        <id>/2022/09/25/private-repo-github-blog-with-github-actions</id>
        <link href="https://parkgang.github.io/blog/2022/09/25/private-repo-github-blog-with-github-actions"/>
        <updated>2022-09-25T11:00:00.000Z</updated>
        <summary type="html"><![CDATA[Private Repo 으로 GitHub Blog (GitHub Pages) 를 만들기 위해서는 GitHub 계정을 Pro로 업그레이드해야 합니다. 근데, 계정을 업그레이드하지 않고 SSH 와 GitHub Actions 을 이용해서 Private 으로 GitHub Blog 를 만들 수 있다는 사실을 아시나요? 이번 글에서 소개해 보도록 하겠습니다.]]></summary>
        <content type="html"><![CDATA[<p><code>Private Repo</code> 으로 <code>GitHub Blog</code> (GitHub Pages) 를 만들기 위해서는 GitHub 계정을 Pro로 업그레이드해야 합니다. 근데, 계정을 업그레이드하지 않고 <code>SSH</code> 와 <code>GitHub Actions</code> 을 이용해서 <code>Private</code> 으로 <code>GitHub Blog</code> 를 만들 수 있다는 사실을 아시나요? 이번 글에서 소개해 보도록 하겠습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="원리">원리<a class="hash-link" href="#원리" title="제목으로 바로 가기">​</a></h2><p>원리는 단순한데 블로그 코드를 운영할 <code>Private</code> Repo의 코드를 <code>GitHub Pages</code> 으로 사용될 <code>Public</code> Repo으로 <code>Push</code> 하는 방법입니다.</p><p>매번 <code>Private</code> Repo의 코드를 <code>GitHub Pages</code> 가 사용되는 <code>Public</code> Repo으로 이동하기는 번거롭겠죠? 이를 <code>GitHub Actions</code> 을 이용해서 자동 할 것입니다.</p><p><code>Public</code> Repo에 접근할 수 있도록 <code>SSH Key</code> 를 발급받고 <code>Private</code> Repo의 <code>Push</code> 가 발생하면 <code>GitHub Actions</code> 이 <code>Public</code> Repo으로 <code>SSH</code> 를 이용해서 <code>Push</code> 하도록 만들 것입니다.</p><p>핵심적으로 알아야 될 것은 <code>A</code> 라는 <code>Repo</code> 가 <code>B</code> 라는 <code>Repo</code> 로 <code>commit</code> 할 때 <code>SSH</code> 으로 어떻게 <code>자격 증명</code> 을 하느냐입니다.</p><p>방법은 아래의 <code>설계</code> 단계에서 보도록 하겠습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="설계">설계<a class="hash-link" href="#설계" title="제목으로 바로 가기">​</a></h2><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>기본적으로 <code>SSH</code> 가 무엇인지, <code>SSH 공개키 인증</code> 을 알고 있다는 가정으로 설명되었습니다.</p></div></div><ul><li><code>SSH Key</code> 생성을 위해서는 <code>ssh-keygen</code> 를 사용합니다.</li><li><code>id_rsa</code> (private key) 와 <code>id_rsa.pub</code> (public key) 으로 서로 인증하는 것입니다.</li><li><code>GitHub Pages Repo</code> (Public Repo) 에 Deploy key으로 <code>public key</code> 를 등록하고 <code>Blog Code가 존재하는 Repo</code> (Private repo) 에 <code>private key</code> 를 이용해서 <code>commit</code> 하는 것입니다.</li><li>그리고 이 작업을 <code>GitHub Actions</code> 을 이용하여 자동화합니다.</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="구현">구현<a class="hash-link" href="#구현" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="ssh-key-생성">SSH key 생성<a class="hash-link" href="#ssh-key-생성" title="제목으로 바로 가기">​</a></h3><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p><a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#generating-a-new-ssh-key" target="_blank" rel="noopener noreferrer">Generating a new SSH key and adding it to the ssh-agent - GitHub Docs</a> 를 참고할 수 있습니다.</p></div></div><p>아래의 명령을 수행합니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Key를 생성하려는 경로로 이동: 다른 SSH와 겹치지 않기 위함</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">cd</span><span class="token plain"> ~/Downloads</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Key 생성</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ssh-keygen -f id_rsa -t ed25519 -C </span><span class="token string" style="color:#e3116c">"your-github-email@test.com"</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>여기서 <code>Key</code> 생성 시 암호를 지정하지 않도록 합니다. 그냥 <code>Enter</code> 치면 됩니다.</p><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>이유는 <code>GitHub Actions</code> 에서 사용하는 <code>webfactory/ssh-agent@v0.5.0</code> 에서 <code>Error: Command failed: ssh-add -</code> 가 발생합니다.</p><p>관련 Issue: <a href="https://github.com/webfactory/ssh-agent/issues/54" target="_blank" rel="noopener noreferrer">Command failed: ssh-add </a></p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAxklEQVQYlWWOy3KDMBAEiU0APZCQAIEMGMqJSf7/Bzsl5RJXDl17mKnpLawLbPsHtbRUTUtZKa7v8h9FozqU6dFmyMXiUvN2bbiU4oWiEgZtetpuzKu1MHkhlf9SpKDrZ9xwy9ePC7L1eeVFnbT3eeO+P1mPk+042R/fjPMd4yasnzNZXdYaH1Z8WOiGSOsmlB1ImdCORna/aiUM67yyxI0YbgxupFUWmX6uFFVSC2kJxvMVIuey8xk3nnHjCJHJT1hp0LXmBzEJXY2ZyWXuAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1012" height="874"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.7d921f4.1012.png" srcset="/assets/ideal-img/1.7d921f4.1012.png 1012w" width="1012" height="874"></noscript></div></div></div></div><p><code>Key</code> 가 생성되면 아래와 같이 <code>파일</code> 이 보이실 것입니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAt0lEQVQYlXWMvQrCMBhFE6ul+IegiJObgzYp1SRtpKbawcWhDk4+gKPgC/jq95NiFRUcznI597D99Y7J6YbRrkQQa/iRRatCWvDQoCk0fKnAhtmexkVJo/xA/SSnntlSsNoQC5MaQ1wYYoO0QE87dLWDH2doSAtPWrBFAi5SeJEFCxOwtsqprRw1pH0WFuZJXeSiLjcjS51KrIYP4X144cUZtZaOmFx/i7/o8kyz8kLT4kh8rv6KDyCpZSTn/scnAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1252" height="870"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.634598e.1252.png" srcset="/assets/ideal-img/2.634598e.1252.png 1252w" width="1252" height="870"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="public-repogithub-pages-repo-에-key-등록">Public Repo(GitHub Pages Repo) 에 Key 등록<a class="hash-link" href="#public-repogithub-pages-repo-에-key-등록" title="제목으로 바로 가기">​</a></h3><p>생성된 <code>id_rsa.pub</code> 내용을 복사해서 <code>Public Repo(GitHub Pages Repo)</code> 에 <code>Deploy key</code> 에 <code>public key</code> 를 등록합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAZ0lEQVQImT2MSQ6EMAwE8wAgu00IhMP8/401ckAcWnbLVXapdqoOsnRiaXNqG4SkrKGyRUG046wsPpOkI22wRsEnRY6bdv0oeuKj4MwwOMtJ2S8WX75PW6hzt0zQjmbux/0Ab6bwSn+qLjE5x3PNPAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2986" height="1336"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.f6ed08d.2986.png" srcset="/assets/ideal-img/3.f6ed08d.2986.png 2986w" width="2986" height="1336"></noscript></div></div><p>push을 위해서 <code>Allow write access</code> 를 체크해야합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAl0lEQVQImVWPQQ6CMAAEq0IplLYUCwVJmngw4sEbb/D/PxoDmhAOk0k2u4cVMb2IaSbdn7h2wDQdpumx/oiIY6INA66NaNtS1R5tr9ht0O3F2q1hQKqaLC/JZLWTl+SFRiqLOGUKcS4Ql78P/LJzViK86fA2YOoGox1Gr27QlUOWdkNVDrF8Fh7vmT7ciHEi9tPmEMbDmS+gR0aI8aQ0wAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1588" height="958"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.ce16ced.1588.png" srcset="/assets/ideal-img/4.ce16ced.1588.png 1588w" width="1588" height="958"></noscript></div></div><p>정상적으로 등록된 것을 확인할 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAR0lEQVQImR2LSQ6AIBDAuBtBtmE3RBP//8MaOPTStCq3SR0vITW8NIwVziuirWyWi+VG+dQZ86OOh9InxgmHdjtag/WZECs/8loZJQ1jalUAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1582" height="396"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.815b59e.1582.png" srcset="/assets/ideal-img/5.815b59e.1582.png 1582w" width="1582" height="396"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="private-repoblog-코드가-존재하는-repo-에-key-등록">Private Repo(Blog 코드가 존재하는 Repo) 에 Key 등록<a class="hash-link" href="#private-repoblog-코드가-존재하는-repo-에-key-등록" title="제목으로 바로 가기">​</a></h3><p>생성된 <code>id_rsa</code> 내용을 복사해서 <code>Private Repo(Blog 코드가 존재하는 Repo)</code> 에 <code>secrets</code> 에 <code>private key</code> 를 등록합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAbElEQVQImR2NURLEIAhDPUDbrQpahbUz3d7/itkJf0DeC6moochA0YksA7UZ7Psg1wtHbhCdmHMh7adi+0gAtXvAtl7Y+sHvF8MfSDMkWjvB7pDuIR25ozSLnSLLAtxOjcbL7njHgDIlzrz9AeKOMdSzoiM3AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2992" height="1334"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.916abae.2992.png" srcset="/assets/ideal-img/6.916abae.2992.png 2992w" width="2992" height="1334"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAiklEQVQImWXPWwrCMBBG4aFj0jTT3GuDVQoFheoa3P+2fqkoKH343s7LoTqvGOcV03zF6bLAxeObT+MfGuoZqVQYiTCSIL6gDwN8rvBp8wljrnChwMcBnQToVqDNLwdjA4hZgZsGzAwi2ms0WFmQSxk+ZUgfoVqLg5Yd3XnQ7fnAdF9gpewGNt+xF4yGRZ5K4bh9AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1584" height="922"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.8635bbf.1584.png" srcset="/assets/ideal-img/7.8635bbf.1584.png 1584w" width="1584" height="922"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAXElEQVQImT3MSw6AIBADUE6gIjggDPITo/H+56uBhYvmJU1awfFEqjcUeUySMK/mjzaMcr3g2CBCvpDrg25I7ZdTA/kyDvpIyM1COQdJ+ygWZYcreWh7wHGGMYwPTiInE0XcmmQAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1588" height="496"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.e7e2462.1588.png" srcset="/assets/ideal-img/8.e7e2462.1588.png 1588w" width="1588" height="496"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="github-actions-등록">GitHub Actions 등록<a class="hash-link" href="#github-actions-등록" title="제목으로 바로 가기">​</a></h3><p><code>Private Repo</code> 에 아래의 코드를 참고하며 <code>GitHub Actions</code> 을 등록합니다.</p><p>저는 <code>Node.js</code> 기반이라 <code>yarn</code> 을 사용하여 <code>Build</code> 하는 코드가 들어가 있는데 프로젝트 상황에 맞게 변경해서 사용하면 됩니다.</p><div class="language-yaml codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">.github/workflows/deploy.yml</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-yaml codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Deploy to GitHub Pages</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">on</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">push</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">branches</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> main</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">env</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">USER_EMAIL</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> your</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">github</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">email@test.com</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">USER_NAME</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> your</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">github</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">jobs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">deploy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">runs-on</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">latest</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">steps</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">uses</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> actions/checkout@v2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">uses</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> actions/setup</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">node@v3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">with</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">node-version</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 16.x</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">cache</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> yarn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">uses</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> webfactory/ssh</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">agent@v0.5.0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">with</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">ssh-private-key</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> secrets.GH_PAGES_DEPLOY </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Deploy to GitHub Pages</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">env</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">USE_SSH</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">run</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          git config --global user.email ${{ env.USER_EMAIL }}</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          git config --global user.name ${{ env.USER_NAME }}</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          yarn install --immutable</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          yarn deploy</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="아쉬운-점">아쉬운 점<a class="hash-link" href="#아쉬운-점" title="제목으로 바로 가기">​</a></h2><p>여기까지 따라오셨다면 이제 <code>Private</code> 에서 수정 발생 시 <code>GitHub Pages</code> 의 <code>Repo</code> 에 자동으로 <code>Push</code> 되어서 배포가 이뤄질 것입니다!</p><p>이렇게만 본다면 문제가 없어 보이지만 아쉬운 점도 존재합니다.</p><ul><li>블로그 코드를 관리하는데 <code>Public</code> 과 <code>Private</code> 으로 나눠져서 관리 포인트가 증가한다.</li><li><code>Private Repo Push</code> → <code>GitHub Actions</code> → <code>Public Repo GitHub Pages Deploy</code> 과정을 거치기 때문에 느리다</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마치며">마치며<a class="hash-link" href="#마치며" title="제목으로 바로 가기">​</a></h2><p>이렇게 <code>Private</code> Repo의 코드를 <code>Public</code> Repo에 <code>Push</code> 함으로써 <code>Private</code> 으로 <code>GitHub Blog</code> 개발 방법에 대해서 설명하였습니다.</p><p><code>GitHub Blog</code> 때문에 요금제 업그레이드는 부담스럽거나, 개인 설정 같은 것 때문에 <code>Private</code> 으로 <code>Blog</code> 를 관리해야 하는 사람에게는 도움이 될 수 있기를 바랍니다.</p><p>더 나아가 <code>SSH</code> 와 <code>GitHub Actions</code> 을 이렇게 사용할 수 있구나 예제를 얻어 갈 수 있으시면 좋을 거 같습니다.</p><p>여기까지 읽어주셔서 감사합니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="reference">Reference<a class="hash-link" href="#reference" title="제목으로 바로 가기">​</a></h2><p><a href="https://opentutorials.org/module/432/3742" target="_blank" rel="noopener noreferrer">SSH Key - 비밀번호 없이 로그인 - 원격제어</a></p>]]></content>
        <category label="blog" term="blog"/>
        <category label="GitHub Pages" term="GitHub Pages"/>
        <category label="GitHub Actions" term="GitHub Actions"/>
        <category label="SSH" term="SSH"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[마음 편한 코드 리뷰를 위한 선택: husky와 lint-staged 도입기]]></title>
        <id>/2022/06/24/easy-code-review-with-husky-lint-staged</id>
        <link href="https://parkgang.github.io/blog/2022/06/24/easy-code-review-with-husky-lint-staged"/>
        <updated>2022-06-24T11:00:00.000Z</updated>
        <summary type="html"><![CDATA[코드 리뷰 할 때마다 보이는 지켜지지 않은 컨벤션… 심지어 타입 오류로 빌드도 안되는 상태… 이를 해결하기 위한 husky 와 lint-staged 도입 계기와 설정 방법을 소개합니다.]]></summary>
        <content type="html"><![CDATA[<p>코드 리뷰 할 때마다 보이는 지켜지지 않은 컨벤션… 심지어 타입 오류로 빌드도 안되는 상태… 이를 해결하기 위한 <code>husky</code> 와 <code>lint-staged</code> 도입 계기와 설정 방법을 소개합니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="개요">개요<a class="hash-link" href="#개요" title="제목으로 바로 가기">​</a></h2><p>익히 <code>husky</code> 와 <code>lint-staged</code> 사용은 프로젝트의 코드 품질과 일관성을 유지하기 위해 사용되는 글이 많습니다.</p><p>물론 그것을 중점으로 도입되는 것은 맞지만 다른 관점으로 코드 리뷰어로써 도입하게 된 배경을 소개해 보려고 합니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="도입-배경">도입 배경<a class="hash-link" href="#도입-배경" title="제목으로 바로 가기">​</a></h2><p>당시 저는 <code>TS</code> + <code>Next.js</code> + <code>MUI</code> 를 이용한 <code>프로모션 사이트</code> 의 <code>메인 코드 리뷰어</code> Role으로 활동 중이었는데 처음에는 큰 문제가 없었지만 점차 문제가 발생하기 시작했습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="나만-열심히-사용하는-prettier--eslint">나만 열심히 사용하는 <code>Prettier</code> , <code>ESLint</code><a class="hash-link" href="#나만-열심히-사용하는-prettier--eslint" title="제목으로 바로 가기">​</a></h3><p>친절히 개인이 사용하는 코드 Editer (like. <code>IDE</code>) 에 설정을 하고 쓰면 좋겠지만 그렇지 않은 경우가 꽤 있었습니다.</p><p>사실 <code>prettier</code> 까지는 <code>포멧팅</code> 정도이기에 그냥 넘어갈 수 있지만 (눈물을 흘리며 내가 <code>npx prettier --write .</code> 실행하기…)</p><p><code>ESLint</code> 의 검사를 하지 않는 것은 문제가 발생했습니다.</p><p>개발을 위해 <code>MUI</code> 를 사용하고 있는데 <code>import { Button } from '@mui/material';</code> 와 같이 최상위에서 가져오면 아직 사용하지 않을 <code>Component</code> 까지 번들링되어서 전체적인 <code>DX</code> 경험과 <code>Build</code> 시간이 길어지는 문제가 발생했습니다. (가뜩이나 <code>MUI</code> 느린데 말이죠!)</p><p>이를 위해서는 <code>MUI</code> 에서 <a href="https://mui.com/material-ui/guides/minimizing-bundle-size/#option-one-use-path-imports" target="_blank" rel="noopener noreferrer">가이드 하는 방법 중 하나</a>로 <code>import Button from '@mui/material/Button';</code> 와 같이 <code>use path imports</code> 를 해야 했습니다.</p><p>개발하다 보면 나도 모르는 사이에 최상위에서 <code>import</code> 를 하고 있을 수 있고 이는 전체적인 <code>DX</code> 와 <code>build</code> 퍼포먼스 저하로 이뤄지는데 시스템적으로 개선이 필요했습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="type-error">Type Error!<a class="hash-link" href="#type-error" title="제목으로 바로 가기">​</a></h3><p><code>TS</code> + <code>Next.js</code> 을 사용했는데 <code>npm run dev</code> 으로 수행된 <code>development</code> 상태에서는 프로젝트의 특정 파일 중 타입 에러가 발생하더라도 내가 수정하고 있는 페이지에 대해서 문제가 없다면 화면에 잘 표시되었기에 마치 프로젝트가 문제가 없는 것처럼 보이는 문제가 있었습니다.</p><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>이것은 예시로 보고 있는 페이지에서도 타입 에러가 발생하더라도 <code>npm run dev</code> 에서는 정상적으로 보이지만 <code>npm run build</code> 시 <code>type error</code> 이 발생하는 것을 볼 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAkElEQVQImV3MzQ6DIBhEUWxVqMivn4IiJbZ9/1ecRjaNXdzM5mQYhSf8nCA1QdmA0S6QZsa87jg+b5RSkHMGo5ARtwNTyNA+Vnhu/xjBGPvllwRadlAosLRVpP0KMeg/GF8wsWBwE4R0tRNLZa9QU6qQjwZNO4A1HE0nce/EFRpKkNMKrhxarnHjqr72Ql7gF+RhRQQ1fIbGAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3584" height="2240"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.53ad5b0.3584.png" srcset="/assets/ideal-img/1.53ad5b0.3584.png 3584w" width="3584" height="2240"></noscript></div></div></div></div><p>주로 리펙토링 혹은 타입을 확장하면서 많이 발생했는데 이런 경우 <code>build</code> 를 하면 <code>Type error</code> 으로 프로젝트가 망가지기 십상이었습니다.</p><p>애써 <code>PR</code> 이 올라와서 열심히 코드를 보고 있는데 수행이 되면 안 되는 코드들이 보이고, 그런 것을 놓치고 승인을 눌렀더니 <code>build</code> 에 실패해서 다시 코드가 <code>Push</code> 되고 리뷰어로써는 괴로운 순간이었습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="정적-검사를-도입하자">정적 검사를 도입하자<a class="hash-link" href="#정적-검사를-도입하자" title="제목으로 바로 가기">​</a></h3><p><code>코드 리뷰</code> 를 위해 <code>PR</code> (like. 코드 리뷰) 가 올라올 때마다 코드를 리뷰하는 것이 아닌 숨어있는 문제와 <code>숨바꼭질</code> 을 늘 하게 되니 리뷰의 피로도는 높았습니다.</p><p>이러하여 <code>리뷰어</code> 로써의 불편함도 개선하면서 프로젝트의 코드 품질과 일관성을 지키기 위해서 중간에 <code>정적 검사</code> 를 추가하기로 하였습니다.</p><p><code>개발 프로세스</code> 중 어느 이벤트에 추가하느냐는 여러 방법이 있겠지만 <code>프로세스</code> 상 가장 검증이 필요한 <code>git push</code> 단계에 넣는 것이 좋다고 생각했고 이와 관련된 <code>Node.js</code> 계열의 <code>라이브러리</code> 로 <code>husky</code> , <code>lint-staged</code> 를 사용하게 되었습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="husky와-lint-staged는">husky와 lint-staged는?<a class="hash-link" href="#husky와-lint-staged는" title="제목으로 바로 가기">​</a></h2><p>왜 두 개나 사용해야 하나 싶은데 차이점은 <code>husky</code> 는 <code>git hook</code> 을 이용해서 <code>git</code> 의 특정 단계에 대해서 원하는 <code>스크립트</code> 를 수행할 수 있도록 해주고 <code>lint-staged</code> 는 모든 파일에 대해서 <code>정적 검사</code> 를 하면 느리기에 <code>git staging area</code> 만 검사할 수 있도록 해주는 라이브러리입니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="husky-는">husky 는?<a class="hash-link" href="#husky-는" title="제목으로 바로 가기">​</a></h3><p><code>git push</code> 단계에서 특정 <code>스크립트</code> 를 실행시켜서 작업하고 싶은 기능을 제공하는 것은 <code>git hook</code> 입니다.</p><p>근데 <code>git hook</code> 이 <code>.git</code> 안으로 들어가는 형태라서 열심히 <code>hook</code> 을 정의해도 <code>.git</code> 디렉터리는 <code>버전 관리 대상</code> 이 아니므로 공유가 어렵습니다.</p><p>그래서 <code>husky</code> 를 사용하여 손쉽게 공유 및 관리를 달성하는 것입니다.</p><p><code>husky</code> 도 까보면 <code>.git/hooks</code> 에 있는 스크립트를 <code>.husky</code> 으로 돌리는 것이라고 합니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="lint-staged-는">lint-staged 는?<a class="hash-link" href="#lint-staged-는" title="제목으로 바로 가기">​</a></h3><p>모든 파일에 대해서 <code>정적 검사</code> 를 해도 좋은 경우와 그렇지 않은 경우가 있을 것입니다.</p><p>예를 들어 <code>prettier</code> 는 전체 프로젝트에 대해서 한번 진행이 되었다면 수정되는 파일에 대해서만 진행하는 것이 경제적입니다.</p><p>이제 이럴 때 <code>lint-staged</code> 을 이용해서 <code>commit</code> 하려고 하는 (수정이 된 파일) 에 대해서만 추가 작업을 진행할 수 있는 패키지입니다.</p><p>헷갈리면 안 될 것이 <code>lint-staged</code> 는 <code>스테이징 영역에 있는 파일 리스트에 대해서 추가 작업을 정의할 수 있도록 해주는 도구</code> 입니다.</p><p><code>스테이징 영역</code> 으로 들어갔을 때 <code>hook</code> 으로 실행해 주는 것이 아니므로 왜 <code>스테이징</code> 으로 들어갔는데 실행 안되지? 라고 햇갈리지 않도록 합니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="원리-및-적용-방법">원리 및 적용 방법<a class="hash-link" href="#원리-및-적용-방법" title="제목으로 바로 가기">​</a></h2><p>시간에 지남에 따라 적용 방법은 바뀔 수 있기에 <code>공식 문서</code> 를 보는 것을 추천드립니다.</p><p>적용 방법은 심플하게 설명하고 적용하면서 알게 된 원리와 주의점에 대해서 설명을 해보겠습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="husky-automatic-방법으로-설치">husky Automatic 방법으로 설치<a class="hash-link" href="#husky-automatic-방법으로-설치" title="제목으로 바로 가기">​</a></h3><ol><li><p>공식 문서에서 권장하는 방법인 <a href="https://typicode.github.io/husky/getting-started.html#automatic-recommended" target="_blank" rel="noopener noreferrer">Automatic</a> 을 실행합니다.</p></li><li><p>그러면 아래와 같이 설치가 될 텐데 <code>prepare</code> 를 이용해서 프로젝트 설치 전 <code>husky</code> 내용을 설치하도록 하는 것을 볼 수 있습니다.</p><div class="language-json codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">package.json</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-json codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"scripts"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"prepare"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"husky install"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"format"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"prettier --write"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"lint"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"eslint --fix"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"type-check"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tsc --pretty --noEmit"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"lint:full-inspection"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"next lint --fix"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"devDependencies"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"husky"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"^8.0.0"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>또한, 샘플로 <code>pre-commit</code> 가 생기는 것을 볼 수 있습니다. <code>pre-commit</code> 이외도 다른 <code>git hook</code> 을 이용한 파일을 생성해서 <code>hook</code> 을 정의할 수 있습니다.</p><div class="language-bash codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">.husky/pre-commit</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-bash codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token shebang important">#!/usr/bin/env sh</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">.</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable function" style="color:#d73a49">dirname</span><span class="token string variable" style="color:#36acaa"> -- </span><span class="token string variable string" style="color:#e3116c">"</span><span class="token string variable string variable" style="color:#36acaa">$0</span><span class="token string variable string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">)</span><span class="token string" style="color:#e3116c">/_/husky.sh"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> </span><span class="token builtin class-name">test</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>이외 <code>_</code> 내부에 있는 <code>husky.sh</code> 를 보면 git hook과 관련된 것은 모두 <code>husky</code> 에서 사용할 수 있도록 <code>Overwrite</code> 하고 있는 것을 볼 수 있습니다.</p></li></ol><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="pre-commit-hook에-lint-staged-지정"><code>pre-commit</code> hook에 <code>lint-staged</code> 지정<a class="hash-link" href="#pre-commit-hook에-lint-staged-지정" title="제목으로 바로 가기">​</a></h3><p>아까 생성된 <code>pre-commit</code> 파일 하단에 <code>스크립트</code> 를 작성하면 <code>commit 을 실행하기 전에 실행</code> 때 원하는 작업을 <code>Local</code> 에서 수행할 수 있습니다.</p><p>여기서는 <code>commit</code> 시 <code>git staging area</code> 에 올라온 파일에 대해서만 특별한 작성을 하기 위해서 <code>lint-staged</code> 를 사용하기 때문에 아래와 같이 명시했습니다.</p><div class="language-bash codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">.husky/pre-commit</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-bash codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token shebang important">#!/usr/bin/env sh</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">.</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable function" style="color:#d73a49">dirname</span><span class="token string variable" style="color:#36acaa"> -- </span><span class="token string variable string" style="color:#e3116c">"</span><span class="token string variable string variable" style="color:#36acaa">$0</span><span class="token string variable string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">)</span><span class="token string" style="color:#e3116c">/_/husky.sh"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># `.lintstagedrc.js` 규칙에 맞게 검사</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">npx lint-staged</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="lint-staged-설치-및-지정">lint-staged 설치 및 지정<a class="hash-link" href="#lint-staged-설치-및-지정" title="제목으로 바로 가기">​</a></h3><ol><li><p><code>lint-staged</code> 를 설치합니다.</p><div class="language-bash codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-bash codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> lint-staged --save-dev</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p><code>.lintstagedrc.js</code> 을 생성하여 어떤 파일을 어떻게 체크할 것인지 정의합니다.</p><div class="language-js codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">.lintstagedrc.js</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-js codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// script 실행만을 위함이므로 기본 패키지 매니저인 npm으로 하는 것이 깔끔합니다</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">"*"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"npm run format"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">"*.@(js|jsx|ts|tsx)"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"npm run lint"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li></ol><p>수동으로 <code>lint-staged</code> 를 수행하는 방법은 <code>npx lint-staged</code> 가 있습니다.</p><p>위에서 <code>pre-commit</code> 에 <code>npx lint-staged</code> 를 지정했기 때문에 <code>commit</code> 시 <code>npx lint-staged</code> 가 실행되어 <code>스테이징</code> 에만 있던 파일은 <code>.lintstagedrc.js</code> 에 정의된 대로 작업이 수행될 것입니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="pre-push-hook으로-type-검사-진행"><code>pre-push</code> hook으로 <code>type</code> 검사 진행<a class="hash-link" href="#pre-push-hook으로-type-검사-진행" title="제목으로 바로 가기">​</a></h3><p>왜, <code>type</code> 검사는 <code>pre-push</code> 에서 하는지 의문일 수 있습니다.</p><p><code>pre-commit</code> 에서 하기에는 매번 <code>commit</code> 마다 확인하기에는 부담스러운 작업이고, <code>lint-staged</code> 에서 진행하기에는 <code>스테이징</code> 에만 올라온 파일만 검사하기 때문에 문제가 발생합니다.</p><p><code>lint-staged</code> 에서 진행하면 아래의 문제가 발생하는데</p><ul><li>예시로 <code>a.ts</code> 는 <code>b.ts</code> 의 파일을 <code>import</code> 합니다.</li><li>여기서 <code>a.ts</code> 만 수정되어 <code>스테이징</code> 에 올라가면 어떻게 될까요?</li><li>타입 검사는 에러가 발생합니다. 이유는 <code>a.ts</code> 만 검사를 하기 때문에 <code>b.ts</code> 가 없다고 말이죠</li></ul><p>때문에 <code>Push</code> 으로 <code>Remote Server</code> 으로 보내기 전 <code>pre-push</code> 으로 검사하는 것이 깔끔합니다.</p><p>기호에 맞게 수정하면 되는데 아래와 같이 구성할 수 있습니다.</p><div class="language-bash codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">.husky/pre-push</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-bash codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token shebang important">#!/usr/bin/env sh</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">.</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable function" style="color:#d73a49">dirname</span><span class="token string variable" style="color:#36acaa"> -- </span><span class="token string variable string" style="color:#e3116c">"</span><span class="token string variable string variable" style="color:#36acaa">$0</span><span class="token string variable string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">)</span><span class="token string" style="color:#e3116c">/_/husky.sh"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 타입 검사의 경우 모듈 의존성 때문에 모든 파일을 대상으로 진행되어야 합니다. 때문에 `lint-staged` 으로 타입 검사 시 스테이징에 올라온 파일만 검사하다 에러가 발생하므로 push 이전에 확인하도록 합니다.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run type-check</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># next에서 검사하는 lint도 `pages` 디렉터리가 있냐 없냐와 같은 전체 파일을 대상으로 검사하기 때문에 `lint-staged` 으로 검사가 어려워 push 이전에 확인하도록 합니다.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run lint:full-inspection</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="insight">Insight<a class="hash-link" href="#insight" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="lint-staged-에서-prettier-이나-eslint-실행-시--으로-전체-파일에-대한-경로를-지정하면-의미가-없습니다"><code>lint-staged</code> 에서 <code>Prettier</code> 이나 <code>ESLint</code> 실행 시 <code>.</code> 으로 전체 파일에 대한 경로를 지정하면 의미가 없습니다.<a class="hash-link" href="#lint-staged-에서-prettier-이나-eslint-실행-시--으로-전체-파일에-대한-경로를-지정하면-의미가-없습니다" title="제목으로 바로 가기">​</a></h3><p><code>lint-staged</code> 를 사용하면서 가장 중요한 부분이라고 생각되는데 <code>ESLint</code> 를 예로 든다면 <code>eslint --fix .</code> 시 <code>.eslintignore</code> 를 제외한 파일에 대해 모두 검사한다는 것 입니다.</p><p>우리가 <code>lint-staged</code> 를 사용해서 얻는 이점은 <code>스테이징</code> 에 올라온 파일만 검사하는 것인데 위와 같이 아예 <code>.</code> 을 옵션으로 넘겨주는 순간 의미가 없어집니다.</p><p>고로 아래와 같이 정의하면 <code>js|jsx|ts|tsx</code> 에 해당하는 파일만 <code>옵션</code> 으로 전달되어 검사하게 되어 <code>스테이징</code> 에 있는 파일만 지정하여 검사할 수 있는 것입니다.</p><div class="language-js codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">.lintstagedrc.js</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-js codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">"*.@(js|jsx|ts|tsx)"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"eslint --fix"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="windows-에서-husky-생성한-경우-linux-에서-동작하지-않는-문제"><code>Windows</code> 에서 <code>husky</code> 생성한 경우 <code>Linux</code> 에서 동작하지 않는 문제<a class="hash-link" href="#windows-에서-husky-생성한-경우-linux-에서-동작하지-않는-문제" title="제목으로 바로 가기">​</a></h3><p><code>프로젝트</code> 에서 <code>타입 에러</code> 가 발생하고 있는데 <code>Push</code> 가 된 것을 보고 <code>Windows</code> 에서 잘 되던 <code>husky</code> 가 <code>WSL</code> 에서 동작하지 않음을 감지했습니다.</p><p>아무 파일이나 수정하고 <code>coomit</code> 하니까 아래와 같은 메시지가 출력되었는데 이는 <code>Push</code> 도 마찬가지로 <code>git hook</code> 이 동작하지 않는 것을 볼 수 있었습니다.</p><div class="codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-text codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">hint: The '.husky/pre-push' hook was ignored because it's not set as executable.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">hint: You can disable this warning with `git config advice.ignoredHook false`.</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>저와 같은 고민을 하는 이슈 ‣ 를 보고 <code>Stackoverflow</code> 를 통해서 아래의 명령어를 통해 권한을 줘서 문제를 해결했습니다.</p><div class="language-bash codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-bash codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">chmod</span><span class="token plain"> ug+x .husky/*</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><code>husky</code> init을 <code>Windows</code> 에서 해서 <code>Linux</code> 에서는 <code>파일 실행 권한</code> 이 없어서 발생한 문제이었는데 나름 여러 <code>환경</code> 에서 개발하다 만난 <code>이슈</code> 라서 신기했습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>이렇게 <code>husky</code> 와 <code>lint-staged</code> 를 왜 도입했는지, 어떻게 설정하는지를 알아보았습니다.</p><p>코드의 규모가 커질수록 <code>일관적</code> 인 코드 품질은 <code>유지보수</code> 성 및 <code>가독성</code> 을 향상시킬 수 있습니다.</p><p>해당 페이지에서 소개한 도구를 사용해서 더 좋은 <code>DX</code> 으로 즐거운 개발을 해나가보세요!</p><p>읽어주셔서 감사합니다.</p>]]></content>
        <category label="husky" term="husky"/>
        <category label="lint-staged" term="lint-staged"/>
        <category label="DevOps" term="DevOps"/>
        <category label="git" term="git"/>
        <category label="코드 리뷰" term="코드 리뷰"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[소프트웨어 개발 능력은 정량적으로 측정이 왜 어려울까? (feat. 전공자가 바라보는 정보처리기사 실기)]]></title>
        <id>/2022/04/24/why-is-software-development-capability-difficult-to-quantitatively-measure</id>
        <link href="https://parkgang.github.io/blog/2022/04/24/why-is-software-development-capability-difficult-to-quantitatively-measure"/>
        <updated>2022-04-24T04:04:49.000Z</updated>
        <summary type="html"><![CDATA[정보처리기사를 공부하다 보면 왜 코딩 능력 보다 단어를 열심히 암기해야 합격할 수 있는 자격증인지 생각하게 됩니다. 그럼 전부 코딩 문제로 출제를 하면 되연 문제가 없을까요? 한번 생각해 봅시다.]]></summary>
        <content type="html"><![CDATA[<p>정보처리기사를 공부하다 보면 왜 코딩 능력 보다 단어를 열심히 암기해야 합격할 수 있는 자격증인지 생각하게 됩니다. 그럼 전부 코딩 문제로 출제를 하면 되연 문제가 없을까요? 한번 생각해 봅시다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="tldr">TL;DR<a class="hash-link" href="#tldr" title="제목으로 바로 가기">​</a></h2><ol><li>정처기 실기를 공부하면서 왜 코딩 능력 보다 단어를 열심히 암기해야 합격 여부가 판가름 되는 자격증인지 이게 과연 IT 분야에서 변별력을 가질 수 있는 자격증인지 생각을 하게 되었다.</li><li>그래서 정처기 실기를 전부 코딩 문제를 내면 정량적인 평가 방법일 까?라는 생각을 하게 되었다.</li><li>그렇게 생각을 해보니 소프트웨어 개발 즉, 프로그래밍이라는 것을 정량적으로 평가하는 것은 어렵다는 결론이 나왔다.</li></ol><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="서론">서론<a class="hash-link" href="#서론" title="제목으로 바로 가기">​</a></h2><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>이하 "정보처리기사" 용어를 "정처기" 라고 말하도록 하겠습니다.</p></div></div><p>IT 업계에서 정보처리기사 유무는 크지 않다는 것으로 알고 있습니다.</p><blockquote><p>있으면 좋지만 없어도 큰 문제가 없다.</p></blockquote><p>그러한 이유는 여러 가지가 있겠지만 필자가 생각하는 이유는 아래와 같다고 생각합니다.</p><ol><li>기사 시험임에 불구하고 관련 학과를 졸업하지 않은 비전공자도 시험에 응시할 수 있다.</li><li>개정 이전까지는 합격률이 높아서 변별력이 없었다.</li><li>정처기라는 자격증이 소프트웨어 개발 능력을 모두 대변하기엔 실무와 괴리감이 있다.</li></ol><p>그리고 개발자라는 직업을 고려할 때 많이 듣는 말로는 <code>평생 공부해야 하는 직업이다</code> 라고 말합니다.</p><p>이 말의 의미는 공부할 것이 많다는 것도 의미하지만 그만큼 개발 패러다임이 많이 변한다는 것도 내포합니다.</p><blockquote><p>1년 전 공부한 게 지금은 쓰이지 않는다...?</p></blockquote><p>이렇게 계속 변하는 학문을 정해진 시험 범위에서 시험을 통해 측정이 가능한가 싶기도 합니다.</p><blockquote><p>물론, 기초적인 CS의 경우는 측정이 충분히 가능할 것입니다.</p></blockquote><p>필자도 정처기 실기를 준비하고 있는데 여러 정보를 얻기 위해서 수제비 카페를 주로 방문합니다.</p><p>근데 여기서 <a href="https://cafe.naver.com/soojebi/87088" target="_blank" rel="noopener noreferrer">전공자분들 중 정처기 시험에 대해 불만의 목소리</a>를 볼 수 있었고 저 또한 정처기가 정말 전공자가 유리한 게 맞는가? 유리하지 않다면 왜 유리하지 않는 것인지 그럼 유리하게 시험 문제를 전부 코딩으로 내보면 어떨까? 라는 내용으로 생각을 공유하면 좋겠다 싶어서 글을 작성해 봅니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="본론">본론<a class="hash-link" href="#본론" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="정처기-시험은-어떻게-진행되는가">정처기 시험은 어떻게 진행되는가?<a class="hash-link" href="#정처기-시험은-어떻게-진행되는가" title="제목으로 바로 가기">​</a></h3><p>우선 정처기 취득을 위해서는 <code>필기</code> 와 <code>실기</code> 의 과정을 거쳐야 하는데 <code>필기</code> 의 경우 객관식이므로 개념에 대해 이해한다면 편하게 풀 수 있기 때문에 넘어가도록 하겠습니다.</p><p>문제는 <code>실기</code> 인데요 이해보다 암기가 필요한 부분이 훨씬 많습니다. 실무에서 많이 사용될만한 용어도 대거 있지만 별로 사용되지 않거나 이런 것도 있구나 싶은 내용과 단어도 많습니다.</p><p>하지만 합격을 위해서는 이런 것도 있구나 하고 넘어갈 수 없습니다. 단답형과 약술형을 많이 맞추기 위해서는 많이 암기할수록 합격률이 높아지기 때문입니다.</p><p>결국 똑같이 새로운 단어를 외워야 하기 때문에 전공자이라서의 큰 메리트는 사라집니다.</p><p>그럼 역시 전공자라서 메리트라면 코딩, SQL 문제라고 생각하는데 20문제 중 평균적으로 6개 정도는 출제되므로 출제 비중이 높은 것을 알 수 있습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="그렇다고-해서-전공자가-유리한가">그렇다고 해서 전공자가 유리한가?<a class="hash-link" href="#그렇다고-해서-전공자가-유리한가" title="제목으로 바로 가기">​</a></h3><p>코딩, SQL 문제 비중이 높다고 해서 전공자가 유리한 가? 라고 생각하면 유리하긴 하지만 큰 상관은 없다고 생각합니다.</p><p>나오는 난이도가 쉬운 편이고 그것을 풀었다고 해서 개발 능력이 입증되는 것은 아니기 때문입니다.</p><p>그러면 정처기 실기의 경우 소프트웨어 개발 능력 입증을 위해서 문제를 전부 코딩, SQL로 출제하고 난이도를 높이면 좋지 아늘까?라는 생각을 할 수 있었습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="전부-코딩-sql-문제로-출제해도-문제일-것이다">전부 코딩, SQL 문제로 출제해도 문제일 것이다.<a class="hash-link" href="#전부-코딩-sql-문제로-출제해도-문제일-것이다" title="제목으로 바로 가기">​</a></h3><p>전부 코딩, SQL 문제로 출제하면 보이는 문제점은 답안 작성도 어렵지만 책점 기준도 모호할 것이라고 생각합니다.</p><p>당장 답안 작성만 생각해도 스트레스일 거 같은데 대학에 가면 주로 요구사항을 주고 손 코딩 문제를 보게 됩니다.</p><p>손 코딩을 할 때 중요하게 생각하는 건 로직, 반복되는 재사용 코드 줄이기 이런 것이 아닌 세미콜론은 잘 적었는지, 오타는 없는지 이런 것들로 점수가 판별되는 경우가 많습니다.</p><blockquote><p>실제 소프트웨어 개발 능력과 없는 것으로 스트레스를 받아야 함</p></blockquote><p>요즘은 개발 도구가 현대적인데 저런 사소한 것을 신경 쓰면서 코딩을 하는 것도 이상합니다. 그럼에도 손 코딩을 보는 이유는 PC으로 코딩을 위한 준비과정 Cost가 발생하며 부정행위를 모니터링하기도 어렵기 때문입니다. 그래서 대학에서 손 코딩을 선호하는 것으로 알고 있습니다.</p><blockquote><p>물론, 모든 대학을 대변하지 않으며 PC로 코딩 시험을 치를 수 있는 좋은 솔루션은 많을 것 입니다.</p></blockquote><p>어찌어찌해서 대부분의 수험생이 요구사항을 구현하고 Input, Output이 정상적으로 동작한다면 이후 변별력은 어떻게 구별할까요? 코드 줄 수가 적다고 해서 좋은 코드일까요? 코드 줄 수는 많은데 단일 책임 원칙으로 정교하게 분리된 코드가 좋은 코드일까요? 혹여나 Feature 중 반만 구현되었다면 점수는 어떻게 채점해야 될까요.</p><p>이처럼 소프트웨어는 바닥부터 차근차근 탑을 쌓듯이 완성되는 제품이며 중간까지만 구현을 마치면 매뉴얼대로 책점이 어렵고 하나의 요구사항에 대해 구현 방법도 여러 가지이기 때문에 소프트웨어 개발에 정답은 없고 이 때문에 정량적인 측정은 어려우며 개발자라는 직업이 경력과 경험이 우대된다고 생각됩니다.</p><p>지금까지 분석한 내용만 봐도 소프트웨어 개발이라는 능력을 정량적으로 측정하는 것은 매우 어렵다고 생각됩니다.</p><blockquote><p>근데 코딩 실력을 정량적으로 평가할 수 있는 솔루션이 있으면 나름대로 대박인 솔루션 이겠는데?</p></blockquote><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="코딩-테스트도-같은-맥락">코딩 테스트도 같은 맥락<a class="hash-link" href="#코딩-테스트도-같은-맥락" title="제목으로 바로 가기">​</a></h3><p>이외 비슷한 사례로 코딩 테스트가 떠오릅니다. 대부분의 회사에서 코딩 테스트를 이용해서 개발자를 판단하는 첫 척도로 사용합니다.</p><p>하지만 이 부분도 알다시피 코딩 테스트 즉, 알고리즘을 잘한다고 해서 개발을 꼭 잘하는 것은 아닙니다.
이 부분에 대해서는 여러 의견이 분분하지만 <strong>확실한 것은 잘하면 좋은 것이며 알고리즘이 중요한 것은 맞습니다.</strong></p><p>그럼에도 코딩 테스트라는 나름 정량적인 테스트를 통해 합격자를 필터링할 수 있을 것이지만 이 부분에서도 억울해하는 사람이 있을 수도 있는 것입니다. 코딩 테스트가 개발 능력을 전부 대변해 주지 않기 때문입니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="종합하면">종합하면<a class="hash-link" href="#종합하면" title="제목으로 바로 가기">​</a></h3><p>이처럼 소프트웨어 개발 능력을 정략적으로 평가하는 것은 매우 어렵다고 느껴집니다.</p><p>이 때문에 정처기가 있다고 해서 개발의 기본은 하겠지라는 것을 보장할 수 없으며 IT 업계에서 정처기 유무는 크게 신경 쓰지 않는 것을 이해할 수 있습니다.</p><blockquote><p>물론 기본은 준비된 최소한의 CS를 아는 사람으로서는 인식될 수 있을 것입니다.</p></blockquote><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="결론">결론<a class="hash-link" href="#결론" title="제목으로 바로 가기">​</a></h2><p>실제 정처기 실기를 준비해 보면 코딩 공부보다는 열심히 단어를 외우고 있는 자신을 볼 수 있습니다.</p><p>정처기 실기를 공부하면서 이런 거 실무에서 안 쓸 거 같은데 왜 해야 되라고 짜증이 날 수도 있지만 위와 같은 insight으로 접근하면 조금의 짜증이 누그러질 수? 있다고 생각합니다.</p><p>누군가는 이 글을 통해 위로가 되었으면? 좋겠네요.</p><p>번외로 당연한 생각이라고 할 수도 있지만 필자는 이런 당연한 생각도 분석을 통해 그 생각의 결과를 글로 export 해서 정리할 수 있는 것도 개발에서 중요한 능력이라고 생각합니다.</p><p>실제 현업에서 개발보다 개발하면서 내가 겪은 문제를 일목요연하게 정리해서 상대방(e.g., 동료, PM)에게 전달하는 커뮤니케이션 능력이 중요하기 때문입니다.</p><p>프로그래밍이라는 것을 공부하면서 이런 것까지 insight를 가지고 대하다 보면 더 좋은 insight이 도출되지 않을까라고 생각됩니다.</p><p>여기까지 읽어주셔서 감사합니다 :)</p>]]></content>
        <category label="자격증" term="자격증"/>
        <category label="정보처리기사" term="정보처리기사"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[parkgang.log(2021)]]></title>
        <id>/2022/01/12/2021-retrospective</id>
        <link href="https://parkgang.github.io/blog/2022/01/12/2021-retrospective"/>
        <updated>2022-01-12T13:02:26.000Z</updated>
        <summary type="html"><![CDATA[정말 바쁘다고 생각했던 2021이 지나갔습니다. 대학 졸업도 하고 여러모로 Big Event가 많아서 한 달 한 달이 묵직했던 거 같아요. 축약해서 이번 연도 회고와 내년을 위한 목표를 정리해 보도록 하겠습니다.]]></summary>
        <content type="html"><![CDATA[<p>정말 바쁘다고 생각했던 2021이 지나갔습니다. 대학 졸업도 하고 여러모로 Big Event가 많아서 한 달 한 달이 묵직했던 거 같아요. 축약해서 이번 연도 회고와 내년을 위한 목표를 정리해 보도록 하겠습니다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>데일리 커밋도 성실히 성공</h5></div><div class="admonition-content"><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAY0lEQVQImSXFWw7CIBBAUTYgjEhMnVIIU/jxEWNidP8ru8b24+Q4rZ06rkxlxafLJvw/7Z9zI2nFHUvBP41wN+J3ED8DefXdw5B3J6wLTqZMMiM1Q9qCLzMH1U3QTLw1ZM78AFiZJ4f2JdUbAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1540" height="402"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.da5a98c.1540.png" srcset="/assets/ideal-img/1.da5a98c.1540.png 1540w" width="1540" height="402"></noscript></div></div></div></div><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>가슴이 웅장해지는 공부량</h5></div><div class="admonition-content"><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAABYlAAAWJQFJUiTwAAAApUlEQVQImS3LORKCQBCFYa4jFrM0PT0bi+VhDLwGBJzEzEsYaqiUEV6DmqitEYMveu8v0IULxe4D5GeJ9p0p4zZoZ3DNAs3hWlRgHpoCu9ix8ZGVcSxq+pFoWSGxrOmZj7f/sAL5ZGOfyLcJbEgS7Sq34F4IMK9cZjkACkyh5dgf2YSGgTwrtEtRCn3aS5hKoYdS6rEUetwJPQrAUYAZFNJUaTx/AeZoSC4h1ZZsAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="4714" height="2246"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.446ad18.4714.png" srcset="/assets/ideal-img/2.446ad18.4714.png 4714w" width="4714" height="2246"></noscript></div></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="tldr">TL;DR<a class="hash-link" href="#tldr" title="제목으로 바로 가기">​</a></h2><ol><li>Go 언어 학습</li><li>CI/CD 고도화 (use k8s)</li><li>첫 제품 제작 느낀 Front-end Engineer로써</li><li>졸업작품 최우수상과 대학교 졸업</li><li>병특 유무는!?</li></ol><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="go-언어와-k8s">Go 언어와 k8s<a class="hash-link" href="#go-언어와-k8s" title="제목으로 바로 가기">​</a></h2><p>TS는 자신 있고 재밌게 사용할 수 있는데 인터프리터 기반의 script 언어만 가지고 있기엔 뭔가 아쉬워서 low-level의 컴파일 언어를 배워보고 싶었습니다.</p><p>딱 떠오르고 핫한 언어가 Rust와 Go가 있었는데 Go가 Docker, k8s와 같이 성공한 프로젝트가 있기도 하고 실제 사용하는 제품이 늘어나는 추세로 보여서 Go를 배우게 되었습니다.</p><p>사실 Go가 그렇게 좋다고 하길래 너무 궁금해서 배워본 것도 있고 DevOps의 Tool 체인들이 Go로 작성된 것도 많아서 배워서 나쁠 것은 없다고 생각했습니다.</p><p>추가적으로 작년에 CI/CD로 자동화 Pipeline까지는 구축할 수 있는데 뭔가 모를 아쉬움이 있어서 더 고도화 시켜보고 싶은 마음에 k8s을 공부하게 되었습니다.</p><p>공부한 k8s을 졸업 작품에 실제로 사용하면서 많은 경험치를 획득할 수 있었는데 배우길 잘했다고 생각이 듭니다.</p><blockquote><p>역시 배포는 너무 재밌어</p></blockquote><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="제품이라고-불릴-수-있는-소프트웨어란-feat-front-end-engineer">제품이라고 불릴 수 있는 소프트웨어란 (feat. Front-end Engineer)<a class="hash-link" href="#제품이라고-불릴-수-있는-소프트웨어란-feat-front-end-engineer" title="제목으로 바로 가기">​</a></h2><p>이번 연도에 직무에서 크게 변한 것은 내가 Front-end Engineer으로 써 제품을 만들 수 있다는 것이었습니다.</p><blockquote><p>아직 부족한 게 많지만...</p></blockquote><p>이전까지는 제품 유지 보수 및 Power App이라는 비 개발자 개발 도구를 이용하여 제품을 만들었었는데 드디어 코드로써 개발에 들어갈 수 있게 되었습니다.</p><p>실제 실무에서 제품 퀄리티의 개발을 하면서 느낀 점이 매우 많았는데 사실 이전까지는 소프트웨어라고 한다면 프로그래밍 언어를 가지고 뭐든 만들면 된다! 실행되고 돌아가면 된다! 문제가 생기면 고쳐주면 되는 거고 프로그래밍 문법을 알면 언제든지 시간을 투자해서 고칠 수 있으니까 문제가 없다!라고 생각했습니다.</p><p>사실 위에 한말은 비즈니스 개발을 한 번도 안 했을 때 발생할 수 있는 착각이었습니다.</p><p>개발자는 항상 바쁘고 일정에 따라 움직이기 때문에 제한적인 시간안에 output을 도출해야 되고 내가 만든 프로그램이 정상적으로 수행되는지 로그로써 남기고 추적도 가능해야 되고 성능도 고려해야 되고 엄청나게 많습니다.</p><p>그냥 프로그래밍 언어를 배워서 서비스 하나 만들어봤어요!로 느낄 수 없는 경험들이 많았습니다.</p><p>그래서 중점적으로 느낀 점이 무엇이냐고 말한다면</p><ol><li>프로그래밍 언어와 프레임워크에 대한 이해도가 중요하다.<ol><li>당연한 말이긴 한데 프로그래밍 언어와 프레임워크에 대한 이해도가 적어도 제품을 만들 수야 있긴 하지만 시간이 지날수록 기술 부체에 부딪히며 일정 산출에 스트레스를 받는 자신을 본다면 무슨 말인지 이해가 될 것입니다.</li><li>백날 언어 문법이나 프레임워크 사용법만 익히더라도 이슈 발생 시 <code>제한된 시간</code> 안에 트러블 슈팅에 성공하려면 정말 많은 경험과 실력이 필요한 것을 느꼈습니다.</li><li>되게 당연할 말을 적어놨는데 지금이라도 실력이 없는 것을 인정하고 공부할 준비가 되었다면 반은 온 것이라고 생각합니다.</li></ol></li><li>모니터링(로깅)<ol><li>내가 만든 제품이 정상적으로 수행되는지 성공률과 실패율 등등 여러 지표를 로깅하고 추적할 수 있는 시스템을 만드는 것이 중요하다는 것을 느꼈습니다.</li><li>실제로 해보니 생각보다 어려운 부분이어서 작년이 CI/CD 고도화이라면 이번 연도는 모니터링, E2E 테스트와 같은 운영성에 초점을 두고 공부하게 될 거 같습니다.</li></ol></li></ol><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="대학교-졸업">대학교 졸업!<a class="hash-link" href="#대학교-졸업" title="제목으로 바로 가기">​</a></h2><p>학교와 회사를 병행하던 대학교의 마지막 학기를 드디어 끝났습니다! 🥳</p><p>아직 공식적으로 졸업장이 나온 것은 아니지만 이제 대학교에 안 간다는 사실이 시원섭섭하지만 개인 시간이 생겼다는 기쁨이 존재합니다.</p><p>졸업작품까지 최우수상에 수상하면서 뿌듯하게 졸업할 수 있을 거 같아요.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>졸업작품 프로젝트를 어떻게 설계했는지 궁금하면 <a href="/series/workflows-with-aks-github-slack/">여기를 클릭해보세요.</a></p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="산업기능요원-병역특례">산업기능요원 (병역특례)<a class="hash-link" href="#산업기능요원-병역특례" title="제목으로 바로 가기">​</a></h2><p>그리고 가장 큰 문제인 군대... 🪖 저는 특성화 고교를 나와서 대학교와 병행하더라도 병특을 받을 수 있는 기회가 있었는데 배정 순위가 낮아서 사실 기대하지 않고 있었습니다.</p><p>하지만 신이 주신 기회인지는 몰라도 여러 운이 겹쳐서 제가 산업기능요원 TO을 받게 되었습니다!</p><p>그래서 사실 산업기능요원 편입 신청이 확정되면 회고를 작성하려고 했는데 문제가 발생했습니다...</p><p>대학교에 재학 중이고 곧 졸업이기 때문에 기사 자격증이 필요하다는 것이었는데 이 때문에 편입 서류에서 탈락되었습니다. 🤯 😱 😵‍💫</p><p>당연히 대학교 재학 중이니까 산업기사나 기사 준비는 당연한 게 아니냐? 할 수 있지만 개인-내부적으로 복잡한 사연이 너무 많았습니다. 하지만 이미 지나간 일이고 덕분에 이번 연도 목표 1순위가 기사 자격증 취득으로 변경되었습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="이외">이외<a class="hash-link" href="#이외" title="제목으로 바로 가기">​</a></h2><ol><li>TDD 하려고 jest도 공부했는데 한 번도 사용하지 못헀다...</li><li>SRE을 경험할 수 있었다.</li><li>이외는 notion에 정리된 study의 모든 것!</li></ol><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="앞으로는">앞으로는?<a class="hash-link" href="#앞으로는" title="제목으로 바로 가기">​</a></h2><p>마일스톤은 단한 게! 정보 처리 기사 취득입니다. 지금까지 열심히 한 것이 아까워서 꼭 병특을 받고 싶네요.</p><p>이제 제품 개발 능력보다는 고도화에 관심이 가고 있습니다. 어떻게 해야지 더 우아하고 적은 코드량으로 개발할 수 있을까? SRE을 통한 운영성도 기르고 싶습니다.</p><p>현재까지 주 업무는 Front-end 이지만 개인적으로 Back-end, DevOps도 계속 공부하고 있고 모두 재밌기 때문에 나는 <code>???</code> 엔지니어에요! 라고 자신있게 말할 포지션을 못 정했습니다.</p><p>내년에는 포지션을 과연 정할 수 있을지? 병특을 받아서 군대에 안 갈지? 관전 포인트가 될 거 같습니다.</p><p>2022년도 화이팅! 하기를 바라며~ 회고록을 마치도록 하겠습니다. 읽어주셔서 감사합니다 :)</p>]]></content>
        <category label="개인 회고" term="개인 회고"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[MSA로 개발해 본 Belf 프로젝트 회고]]></title>
        <id>/2021/12/26/belf-project-retrospective</id>
        <link href="https://parkgang.github.io/blog/2021/12/26/belf-project-retrospective"/>
        <updated>2021-12-26T13:13:10.000Z</updated>
        <summary type="html"><![CDATA[첫 MSA 를 적용하여 만들어진 프로젝트가 성공적으로 종료되어 이에 대한 회고를 해보려고 합니다.]]></summary>
        <content type="html"><![CDATA[<p>첫 <code>MSA</code> 를 적용하여 만들어진 프로젝트가 성공적으로 종료되어 이에 대한 회고를 해보려고 합니다.</p><table><thead><tr><th align="center">Mobile</th><th align="center">PC</th></tr></thead><tbody><tr><td align="center"><div class="imgContainer_g3cu" style="width:170px"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAWAAoDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAUGAwf/xAApEAABAwIEBAcBAAAAAAAAAAABAgMEABEFBhIhBxMUUQgiMUJTlNLh/8QAFQEBAQAAAAAAAAAAAAAAAAAAAwL/xAAbEQEAAgIDAAAAAAAAAAAAAAABAAMCkREhQf/aAAwDAQACEQMRAD8AssZ4Y5Gw3HcNwh1E1UycfIeoTdKQfXSQCe1x3p0rgVlgqJ1yxv8AL/Kt33VolB+RBS64ytXLdccb1NX2sk2uAaZolr0DU2UqtuOYDY1BYL7piZVID1snCofHuQqK863l1pLaCSR1pv3PsrIeIx8DfLafvH8UUUvEKf/Z&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1170" height="2532"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.579a5ea.1170.jpeg" srcset="/assets/ideal-img/1.579a5ea.1170.jpeg 1170w" width="1170" height="2532"></noscript></div></div></td><td align="center"><div class="imgContainer_g3cu"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA+UlEQVQYlW2LMU/CQBhAb9WEtPddvPvuKJQC1wI1tIeouDj7Z1zdNQ4uxsnBXV2Mgcn4GwwzMSHprv8AbPOZrsbhLS/vsez44G4/d9/pcLyO4rSI7Kjo20ERJ6OinwzWk9x9nZ7MHpiU8uVoOqWoG1FuW+TikKRCQkTiHAjlHoVGvTE3Tp/ub67o8uJ8a62twrBTKYWVEKICgG0dez6fM6318yRzlKXpT7vTpWbQJtSGEDUhmlIpJAGwYKFRj2eHQ5olwcYErVKboETUJWpTom5uFGoCgDkzUry6nqGsZ+qzlv/xzjyfX+80/M/dhvcBAMs/1G7FOb/9BYXKV59PMbAqAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2992" height="2244"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/thumbnail.6ba8d5c.2992.png" srcset="/assets/ideal-img/thumbnail.6ba8d5c.2992.png 2992w" width="2992" height="2244"></noscript></div></div></td></tr></tbody></table><p>해당 프로젝트는 졸업과제로 진행되었으며 약 <code>2021.03.10</code> ~ <code>2021.12.18</code> 까지 진행되므로 거즘 1년 동안 진행되었습니다.</p><p>저는 프로젝트에서 PM과 devops를 담당했으며 팀원들이 개발에만 집중할 수 있도록 환경을 만들어주고 <code>front-end</code>, <code>back-end</code> 모두 issue 발생 시 서포트해 주었습니다. 이외 <a href="https://github.com/belf-kr/oauth-server" target="_blank" rel="noopener noreferrer">oauth-server</a> 제품도 개발하였습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="프로젝트-정보-">프로젝트 정보 🔥<a class="hash-link" href="#프로젝트-정보-" title="제목으로 바로 가기">​</a></h2><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>관련 내용이 여러 군대로 파편화되어 링크로 제공되었습니다. 😂</p></div></div><ol><li><a href="https://github.com/belf-kr/.github/blob/main/profile/README.md" target="_blank" rel="noopener noreferrer">소개</a></li><li><a href="https://github.com/belf-kr" target="_blank" rel="noopener noreferrer">project github</a></li><li><a href="https://parkgang.notion.site/6de1a3d7002f4a46ab713e7d6846c490?v=52125eaffbf24e9ca82114b4c2441c80" target="_blank" rel="noopener noreferrer">팀원</a></li><li><a href="https://parkgang.notion.site/36f01d56dff643dfa1db264e33f18d7d" target="_blank" rel="noopener noreferrer">서비스 사용 매뉴얼</a></li><li><a href="https://parkgang.notion.site/Belf-27b87963790b4e43baae2e0c3c6ae123" target="_blank" rel="noopener noreferrer">notion 구경하기 "여기에 모든 정보가 작성되어있어요!"</a></li></ol><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="서비스-접속-주소-">서비스 접속 주소 ✨<a class="hash-link" href="#서비스-접속-주소-" title="제목으로 바로 가기">​</a></h2><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>서버가 down되어 있을 수 있습니다.</p></div></div><ol><li><a href="https://belf.xyz" target="_blank" rel="noopener noreferrer">prod "실 서비스"</a></li><li><a href="https://qa.belf.xyz" target="_blank" rel="noopener noreferrer">qa</a></li></ol><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="background">Background<a class="hash-link" href="#background" title="제목으로 바로 가기">​</a></h2><blockquote><p>사용자의 코스를 공유하는 Todo 서비스</p></blockquote><p>todo의 기능과 GitHub fork에 영감을 받아서 제작된 웹앱 제품입니다.</p><p>이번 프로젝트를 진행하면서 얻을 수 있는 값진 경험치는 k8s의 운영 경험과 정의된 워크플로우 안에서 팀원과 함께 협업하여 제품을 개발한 것입니다.</p><p>CI/CD 및 워크플로우의 경우 내용이 너무 길어서 설계부터 실습까지 <a href="/series/workflows-with-aks-github-slack/blueprint/">blueprint</a> 에 정리해놓았으니 궁금하다면 구경해주세요.</p><p>이번 회고에서는 프로젝트를 진행하면서 전체적으로 경험한 것에 대해 회고하려고 합니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="pm으로써">PM으로써<a class="hash-link" href="#pm으로써" title="제목으로 바로 가기">​</a></h3><p><a href="/series/workflows-with-aks-github-slack/blueprint/">blueprint</a> 에도 명시되어있지만 팀원의 대부분은 이미 개발자로써 직장 생활을 하고 있어서 개발을 어떻게 해야 하는지 개발 프로세스가 왜 중요한 지에 대한 설명까지는 필요가 없는 상태이었습니다.</p><p>적어도 웹앱의 개발 시작에 대한 기초적인 Background가 있는 상태이라서 어떤 개발 프로세스와 역할 배정을 해야지 최고의 효율을 뽑을 수 있을지 고민을 해보았습니다.</p><p>팀원이 좋아하고 자신 있어 하는 stack이 달라서 하나의 언어와 프레임워크로 가기는 어려웠고 MSA를 사용할 만큼 큰 규모의 서비스는 아니지만 다들 작장 생활하느라 개발 주기도 일정하지 않고 그냥 서비스를 하나 독립적으로 담당하는 게 더 좋지 아늘까? 생각이 들었고 무엇보다 제가 MSA으로 프로젝트를 진행해보고 싶었습니다.</p><p>이전에 k8s에 대해 이론 및 간단한 사용법은 공부했는데 실제로 프로젝트에 사용해보지 않아서 사용해보고 싶기도 했고 MSA를 하면서 여러 issue를 접해보고 싶었습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="msa">MSA<a class="hash-link" href="#msa" title="제목으로 바로 가기">​</a></h3><p>MSA는 MicroService Architecture의 줄임말으로써 그냥 쉽게 Service를 Micro하게 잘게 분리하는 Architecture를 말합니다.</p><p>사실 MSA는 이론 자체는 어렵지 않다고 생각합니다. 문제는 Micro 분리하는 Service 기준을 잘 정해야한다는 것이죠 이외 분산된 환경에서 로깅 및 insight를 도출 등 어려운 문제도 많습니다 하지만 해당 Architecture가 지향하는 목표는 심플하다고 생각합니다.</p><p>해당 프로젝트에서는 팀원이 수직적으로 담당할 수 있는 하나의 repo를 기준으로 Service를 분리하였습니다.</p><blockquote><p>즉, repo를 하나의 엔지니어가 담당하는 구조인거죠!</p></blockquote><p>대외적으로 MSA에 알려진 장점으로 뭐 단독 제품으로 쉽게 배포를 할 수 있다 등은 쉽게 체감할 수 있었습니다. 대외적인 것이외 제가 느낄 수 있었던 것의 <code>장점</code> 으로는 엔지니어가 하나의 service를 담당하고 있으니까 커뮤니케이션이 편리하였습니다. 제품에 대해 가장 잘 알고있기도 하고 하나면 집중하면 되는 환경이 되었습니다. <code>단점</code> 으로는 관리 포인트가 엄청나게 증가하였습니다. 사실 이부분은 진행해보니 구조의 결함일 수도 있긴한데 하나의 Service에서 CI/CD에 결함이 포착되었다면? 이외의 Service도 대응해주고 확인을 해줘야했고 MSA를 적용하면서 약간 당연하게? 겪을 수 있는 문제일 수 있지만 Service의 interface 파편화되는 것도 있었습니다.</p><p>그럼에도 불구하고 MSA를 통해서 생산성에 도움은 되었지만 확실히 거대 규모의 Service에서 어울리는 Architecture라는 인상이 강해졌습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="k8s">k8s<a class="hash-link" href="#k8s" title="제목으로 바로 가기">​</a></h3><p>MSA와 찰떡 궁합인 k8s 입니다. 해당 프로젝트에서는 AKS를 사용하여 배포하게 되었습니다.</p><p>실제 서비스를 운영한다는 마음으로 namespace도 정의하고 configmap으로 pod들도 관리하고 L7도 설정하고 해보면서 많은 경험을 얻을 수 있었습니다.</p><p>특히, k8s cluster 사양이 높지 않은데 가뜩이나 <code>qa</code> 와 <code>prod</code> 의 환경을 구분해서 많은 pod이 띄어지면서 발생한 k8s scheduler 배치 이슈 및 container 성능 이슈도 잡아보고 어떻게 object을 네이밍하면 좋을 지도 생각할 수 있는 계기가 되었습니다.</p><p>확실히 운영이라는 것을 해보면서 얻는 경험치는 말로 다 할 수 없는 거 같습니다.</p><blockquote><p>이외에도 <code>prod</code> 에는 mysql replication을 사용해보는 등 재밌는 경험을 해봤습니다.</p></blockquote><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="트러블-슈팅-내역">트러블 슈팅 내역<a class="hash-link" href="#트러블-슈팅-내역" title="제목으로 바로 가기">​</a></h2><p>이외 해당 프로젝트를 진행하면서 발생한 트러블 슈팅 내역은 <a href="https://parkgang.notion.site/Issue-e38806db386346448681eb165327031f" target="_blank" rel="noopener noreferrer">Issue</a> 에 작성되어있습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="느낀-점">느낀 점<a class="hash-link" href="#느낀-점" title="제목으로 바로 가기">​</a></h2><p>k8s와 golang에 대한 제품이 없어서 아쉬웠는데 이번 프로젝트를 통해 만들 수 있어서 기뻤고 <code>qa</code>, <code>prod</code> 환경을 구분해서 <code>prod</code> 이전에 미리 사용성 및 버그를 확인할 수 있어서 미리 대응된 case 들도 있는데 이런 것을 보면서 환경 구분을 잘 해놨구나 하는 뿌듯함도 있었습니다.</p><p>이외에도 CI/CD Pipline을 한번 만들면 영원히 잘 돌아갈 줄 알았는데 중간에 예상하지 못한 곳에서 고장이 난 것도 꽤 신기한 경험이었습니다.</p><blockquote><p>난 영원할 줄 알았는데...</p></blockquote><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>성공적으로 프로젝트는 마무리되었고 많은 경험을 할 수 있었습니다.</p><p>아쉬운 것은 프로젝트가 끝날 때 쯤에서야 팀원들이 온보딩이 완료되어 더 주기적으로 개발하여 좋은 퀄리티의 서비스를 만들 수 있었을 꺼 같지만 다음을 기약하고 넘어가게 되었습니다.</p><p>그래도 이번 프로젝트를 계기로 개발 프로세스부터 배포까지 설계하고 서비스를 운영할 수 있는 자신감이라는 것은 생겼습니다.</p><blockquote><p>물론 로깅관련 부분이 부족해서 빨리 이부분을 보안하고 싶네요!</p></blockquote><p>다음 번에 더 좋은 제품을 만들 수 있도록 기약하며 회고를 마치도록 하겠습니다. 읽어주셔서 감사합니다. 😀</p>]]></content>
        <category label="프로젝트 회고" term="프로젝트 회고"/>
        <category label="MSA" term="MSA"/>
        <category label="졸업작품" term="졸업작품"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[k8s의 EXTERNAL-IP는 Azure에서 어떻게 처리될까?]]></title>
        <id>/2021/12/06/how-is-k8s-external-ip-handled-in-azure</id>
        <link href="https://parkgang.github.io/blog/2021/12/06/how-is-k8s-external-ip-handled-in-azure"/>
        <updated>2021-12-05T20:13:24.000Z</updated>
        <summary type="html"><![CDATA[k8s는 ingress를 통해 외부의 트래픽을 받아올 수 있습니다. 근데 AKS와 같은 클라우드 환경에서 사용하면 ingress IP은 어디서 생성되었으며 어떻게 Load balancing이 되는지에 설명합니다.]]></summary>
        <content type="html"><![CDATA[<p>k8s는 ingress를 통해 외부의 트래픽을 받아올 수 있습니다. 근데 AKS와 같은 클라우드 환경에서 사용하면 ingress IP은 어디서 생성되었으며 어떻게 Load balancing이 되는지에 설명합니다.</p><p>Azure에서 AKS를 사용하면서 문득 이런 궁금증이 생겼습니다.</p><blockquote><p>ingress를 통해 외부의 트래픽을 받아오는데 ingress IP은 어디서 생성되었으며 어떻게 Load balancing이 되는거지?</p></blockquote><blockquote><p>svc를 LoadBalancer으로 생성하는 경우 IP가 생성되는데 어디서 생성된거지? 해당 IP는 어떻게 Load balancing 된다고 장담할 수 있는거지?</p></blockquote><p>Azure에 k8s cluster를 올려놓고 k8s의 spec에 맞게 <code>.yaml</code> 을 적용하면 예상하는 대로 동작이 되어서 Azure가 핸들링 한건지 k8s가 핸들링한건지 큰 관심을 안가지고 있었는데 AKS 관련 내용을 정리하다가 갑자기 궁금해져서 간단하게 찾아보고 정리하려고 합니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="리소스-관련이므로-azure가-하겠지">리소스 관련이므로 Azure가 하겠지?<a class="hash-link" href="#리소스-관련이므로-azure가-하겠지" title="제목으로 바로 가기">​</a></h2><p>IP 생성이랑 Load balancing 이부분은 리소스를 제공하는 Azure만 핸들링할 수 있다고 생각합니다.</p><p>그렇다면 어떤 리소스 타입으로 핸들링하느냐를 찾아야하는대 생각보다 쉽게 찾을 수 있었습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="부하-분산-장치와-공용-ip-주소">부하 분산 장치와 공용 IP 주소<a class="hash-link" href="#부하-분산-장치와-공용-ip-주소" title="제목으로 바로 가기">​</a></h2><p>AKS를 생성하면 자동으로 <code>부하 분산 장치</code> 라는 리소스가 생성됩니다.</p><p>또한, ingress를 설치하고 LoadBalancer type svc를 생성하다 보면 리소스 그룹에서 <code>공용 IP 주소</code> 라는 것을 쉽게 찾아볼 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAMCAYAAABbayygAAAACXBIWXMAAAsTAAALEwEAmpwYAAABkUlEQVQYlSWPS4rbQBiEdQGp1Xp3q/WWrbY1kowfkpxFSK4RyCaQ+wxiMIRAjPcz4APMnGsGyVCDnMVHbar+ql85HHa/jl+/v7X9txdZ7q5p0Vxzubkmi4dr2bQvu333utkefivVfvun3h/Q7ltsdy3KegtZNkiyHItihThOwLn/T1lXzeND3SDPs48kTW+MsRul9GZZ1s00jQ/DMCGC8EmpN82wlCvYtjXyIIbnMViWBcdloIYxWbaN4/HLSVmXxeD7HJpGRtfzwBgD5xyu686BaQ51XXdSpJSDCEKoqjpGYYQ4iiB8H4LzWSfHcf4bi2I5JGkMnZCRiQgiXsNzfeRRiCgMJ48xdF1/UorlcsjyDFTXR8Z9+OECjmkiDgIEQkzzlLtRymKIkhhEVcckSZHmNZJ4hXWWIQ3FxLmP9n5Rysd0sZir303HmyweT/aM406Obb/PT7Vd9zRv/JumGSiloFS/oxMNuq6DEALTNNH3/VkJAvGDEPKsqurZMIzLjKaRi6ZpF01Vz5TS56qqfn4Cu1OTP+MKqwoAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="2890"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.ab5b3ec.2384.png" srcset="/assets/ideal-img/1.ab5b3ec.2384.png 2384w" width="2384" height="2890"></noscript></div></div><p>결론부터 말하면 <code>공용 IP 주소</code> 가 하나의 IP 주소에 대한 리소스이며 k8s svc의 <code>EXTERNAL-IP</code> 를 의미합니다. <code>부하 분산 장치</code> 는 여러개의 <code>공용 IP 주소</code> 를 IP 구성으로 가지고 있습니다.</p><p>그러면 ingress가 생성되거나 혹은 LoadBalancer type svc가 생성되는 것처럼 <code>외부 IP가 필요한 경우</code> Azure가 알아서 <code>공용 IP 주소</code> 를 생성하고 <code>부하 분산 장치</code> 에 바인딩 해주는 거라고 생각할 수 있습니다.</p><blockquote><p>이러니까 이해가 되네...</p></blockquote><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="실제로-그런지-확인해보자">실제로 그런지 확인해보자<a class="hash-link" href="#실제로-그런지-확인해보자" title="제목으로 바로 가기">​</a></h2><p>실제로 그런지 한번 확인해보겠습니다. 위의 캡처 사진은 belf project에서 사용된 리소스 그룹을 캡처한 것인데 외부로 노출되는 svc는 모두 <a href="https://github.com/belf-kr/infrastructure-as-code/tree/v0.3.1" target="_blank" rel="noopener noreferrer">belf-kr/infrastructure-as-code</a> 에서 소비되고 있었습니다.</p><p><code>.yaml</code> 을 읽기 귀찮으신 분들을 위하여 외부로 노출된 svc를 나열해 드리면 아래와 같습니다.</p><table><thead><tr><th>ns</th><th>name</th><th>이유</th></tr></thead><tbody><tr><td>ingress-basic</td><td>nginx-ingress-ingress-nginx-controller</td><td><a href="https://docs.microsoft.com/ko-kr/azure/aks/ingress-tls" target="_blank" rel="noopener noreferrer">HTTPS 수신 컨트롤러</a> 를 사용하기 위함</td></tr><tr><td>qa</td><td>mysql-lb</td><td>qa 환경 mysql 데이터 조회를 팀원도 조회할 수 있도록 외부로 노출하기 위함</td></tr><tr><td>qa</td><td>redis-lb</td><td>qa 환경 redis 데이터 조회를 팀원도 조회할 수 있도록 외부로 노출하기 위함</td></tr></tbody></table><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>kubectl get services --all-namespaces</h5></div><div class="admonition-content"><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAv0lEQVQImSXMywqCQACF4TEIRypvEUGL9irOaM6oM3khfMfaRI8Qbbq8ibgTce1acDGhLg78nMUH9ry46zTvZDeuFcwaneaNGqQNRKyBKK4NL+m2QfoABknfZnwRiscFxFyoJJsGMRMyYgI4oQBW8AXa6fzckFQs3aiHHh80mg+Kx4exIea95IRiYZMXgG70GTXJJpOi0VkbPxnP4sKhP3Cg2dVEUbu2/FJ3abXzWWWgsDJRWKkOKVcobo9JcfsD5IVDPrA+v3MAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3282" height="1828"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2-0.3998d19.3282.png" srcset="/assets/ideal-img/2-0.3998d19.3282.png 3282w" width="3282" height="1828"></noscript></div></div></div></div><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>부하 분산 장치</h5></div><div class="admonition-content"><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAy0lEQVQImQXBwU7DIBgAYB5AIEALtt2oXYmUdQV0abrExMQX87abL+Bx3ozPsqTHJfgA+gQ79PDv+9DTNB6n17f/cXy59PGQujClrR/TowvJDfuLD89/uyF+oG4YTjsfYescGGNA1zVUVQVSKZBSAeMcGGPfqPf+syxL0FpfV3qzSFUshVJLca+WPBNXIQQwLk7IR//VGgMhRFi3PRRlDV27gd5aaNYr4JwDz/If1Dn3/tA0v9bas8yzGeO7GRMyY4xnQuiZUpooJccb6Yo+CWc5MtkAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2688" height="1498"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2-1.59c4fd6.2688.png" srcset="/assets/ideal-img/2-1.59c4fd6.2688.png 2688w" width="2688" height="1498"></noscript></div></div></div></div><p>외부로 노출된 svc는 총 3개인데 위의 리소스 그룹에서는 <code>공용 IP 주소</code> 가 4개가 존재하였습니다. 한개는 뭘까요?</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="aks-slb-공용-부하-분산-장치-사용">aks slb (공용 부하 분산 장치 사용)<a class="hash-link" href="#aks-slb-공용-부하-분산-장치-사용" title="제목으로 바로 가기">​</a></h2><p><code>공용 IP 주소</code> 의 기본 정보를 보니까 태그에 친절하게 k8s metadata 들어가있어서 쉽게 찾을 수 있었습니다.</p><details class="details_rxUF alert alert--info details_PYSW" data-collapsed="true"><summary>공용 IP 주소들 자세히 보기</summary><div><div class="collapsibleContent_oXwz"><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAABcklEQVQYlR3KPVPiQByA8dXSYZNsEmE3RJLwEl4CmLDZkBcId8D4HexkLK6+VhkrR2ks/JjODe3p8ZL/jVTPr3jQsNPyr7n/luY3r+N0vnF7waZquRu76W3cdu/F6w/f2x1vigIR/krnP0BM5zCOUhBiAj2egR9NQSQ58DCCbrf/jDrd9l3negR9n3+JKN7Fab4Lo2THOd9VTfNTkjBIGD8gPxqv7EYD6vXGgTKjoIyBYRhQLpcLQshe08tAVG2NglG4cpw6GLXmgWiVQpKk08gYLWRZ3quqBrKirNEoHq6cugOGaR0orRS6rgNjDEzz6jQSQkCW5TUaDNqrWq0G1St7Tyk96pp61C8rxwo1jkRR/qmqCoqiPCLXbd07lgNh9BMGXgAWMwDjEpRKJcAYFxjjbz8ht9W8FUL8zbLJnyxNt1mabeM43oZhuOWcf3DOD57n/UZnZ+cXQeDbi8XcyvPcms1ya7FcnpxNJt+1kyTB/wGU5HeK0iR5kAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="870" height="842"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.e88cf90.870.png" srcset="/assets/ideal-img/3.e88cf90.870.png 870w" width="870" height="842"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAABb0lEQVQYlR2MO0/CUBhAr24S+m7oE9pqaytXoXBvL70UiqIm/gU3iYOzqxInoywO/kxjWH0B/YwOJ+dMB3WSKO2S9IWPz5/z4nQR4/7C9eNFEB4u4gQ/Ydx5jZP2GPUyejOcToCVU8gHI8iyAtr9IaRsDCQfAaUDaOOjR5QcJFdxpwu42//KGP/hfPjDGPsnjKJPURRBFIU7lDI28/dCCHb31qZlV5Zlg+O6YBhGpev6StMboKjaHPX62SzwA7Bb4VrRzervYNs2mOZ/r1RVA0mW54jknZm3G4DpBuuG0agUTQPDtMB1m5UkSStFUUCSpDlqH8WzyGvCvuusItfZhLa5aVnWpmFYG0WRv1VVBVmW71G0H12HfgC9NIM0pZDRHDzHgVptBwRBrARBgHq9/oDiKLwkNPsYjCbvjPPlYFgsWZ4vKaVLQsgbIWSNMb5FaGu7RnupfzEde2cnE+/suPTOT6deWZZeURR/9jnnwi8fi3hqJ1UlVAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="870" height="842"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.77191c6.870.png" srcset="/assets/ideal-img/4.77191c6.870.png 870w" width="870" height="842"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAABc0lEQVQYlSXMS3OaUBiA4WOXGTlcFQQjICqEM97gHDiI2Blpm/YnZFeni6yzTZysMombLPIzMxm3vXjhy6Su3mf1olHQn4zjyXNWfH3i+XLjk2jTdvyN2yMbPyCPIRm9+EFYoCmj17PlZ2DFEniSA6M5hNEMJkkBlBdAaQphOHxAwUXw0x+NgYyjvyzJdmnKd0nCdoyxned5f0SMAWN8iyZJsnK8Hrhd72C0zMpomWBZbdB1vdI0da9qTZAVdY2mEVs5jgtmp3eQNaPCGINpmmAY/71XFBVESVqjiI9WbtcFs+0c9Gaj0lQVWqdrJYriXpZlEEVxjYZDf9WxO2Cd23vr3Dk2deOoNZpH3TCPsiz9UxQFJEm6Q4NB/5dru5DyEgISgWpYgAUB6vU6CIJQCSffo8Ggd0Up+53n87eU0W2asC3nfEsp3cZx/BrH8YEQcoNqtU9n02jqlOXS/nL5w/52+d0uy9JeLBb2fD7/qJNlmfAOu4N3ygGVihcAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="870" height="842"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.e2bf783.870.png" srcset="/assets/ideal-img/5.e2bf783.870.png 870w" width="870" height="842"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAABeElEQVQYlSXGyW7TQBgA4ClHlLHHnuB4aTJ26tqOrSzOzHiLE6BWoeobcCPiwLnXEnFCNJceeMyqypUli3+EOH0fmkSXsymfPZbL64di0WzDON06LNi6frINo+R7nIx/hFG8RKkUn6ur1yCXbyHPKpBiAfG8gqmsQRRLkLKAUTz+hqJR9DGcTCGepL+zvNpX9Zt9VpR7zvn+wvd/EaKCouB7NMuytXvhg+cNj6Zlt5Zlg+M4YBhG2+3SA+0aoOl0g9K5XLuuB4NhdKSG3WKsgG3bYJpmqyjKQdcpqIRs0LycrN2hB+cD72j2jJZSCpZlQb/fb1VVPWiaBqqqbtB4HK4HbAD2OTs4ffdk9MxT95Vx6pn2SSPkj67rQAj5goLg8pPHPMjyBoJRCtR0QMEYOp0OYIxb/P9fURD4H4SQPxeL+jmXYpdnclcUxU4IseOcP3HOj0mS3KGzsxcv03nqNs0Vu765Ze/e37CmadhqtWJ1Xf/TLcsS/wXTTHfk7QY2OwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="870" height="842"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.87dc6d8.870.png" srcset="/assets/ideal-img/6.87dc6d8.870.png 870w" width="870" height="842"></noscript></div></div></div></div></details><p>type이 <code>aks-slb-managed-outbound-ip</code> 인 리소스이였는데 AKS 클러스터의 송신 트래픽을 허용하기 위한 공용 IP 인거 같습니다.</p><p>해당 부분에 대해서는 더 깊게 찾아보지 않아서 궁금하신 분들은 <a href="https://github.com/Azure/AKS/issues/1281#issuecomment-543867504" target="_blank" rel="noopener noreferrer">Undocumented public IP in AKS-provisioned load balancer</a> 를 읽어보시면 좋을 꺼 같습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>k8s에서 IP를 필요로 할 때 Azure가 어떻게 핸들링 하는지 아주 간단하게 알 수 있었습니다.</p><p>각 클라우드 프로바이더마다 처리 방법이 다르겠지만 그래도 역시 기존에 있던 클라우드 자원을 잘 조립해서 처리하는 방향일 것 같습니다.</p><p>읽어주셔서 감사합니다 :)</p>]]></content>
        <category label="azure" term="azure"/>
        <category label="k8s" term="k8s"/>
        <category label="AKS" term="AKS"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[간단한 HTTP Cache Server를 만들어보자 (with golang)]]></title>
        <id>/2021/11/17/lets-create-an-http-cache-server-with-golang</id>
        <link href="https://parkgang.github.io/blog/2021/11/17/lets-create-an-http-cache-server-with-golang"/>
        <updated>2021-11-16T15:12:13.000Z</updated>
        <summary type="html"><![CDATA[HTTP Cache를 위한 Header 값 설명이 아닌 실제 Server 프로그램이 어떤 로직으로 처리가 되는지 설명합니다.]]></summary>
        <content type="html"><![CDATA[<p>HTTP Cache를 위한 Header 값 설명이 아닌 실제 Server 프로그램이 어떤 로직으로 처리가 되는지 설명합니다.</p><p>HTTP Cache에 대해서 찾아보면 대부분 각 Header가 어떻게 동작하는지에 대해 중점으로 알려줍니다.</p><p>어떻게 보면 Client와 Server가 HTTP Spec에 규정된 Header 값을 바탕으로 캐시하는 것이 전부이긴 하지만 Header 값을 실제 프로그램이 어떻게 읽어서 처리하느냐도 중요합니다.</p><p>필자는 HTTP Cache를 공부할 때 Header에 대한 내용만 나오고 그 Header를 어떤 로직으로 보내는지 서버는 Header를 읽어서 어떻게 하는지 로직적으로 살펴볼 수 없어서 이해가 너무 힘들었습니다.</p><p>특히, <code>ETag</code> 부분은 도데체 어떤 경우에 생기며 <code>ETag</code> 가 변경되는 기준은 무엇이며 검증은 어떤 식으로 하는지 너무 답답했습니다.</p><p>이에 따라 직접 캐시 서버를 만들어서 이해하기로 결심했고 <a href="https://www.sanarias.com/blog/115LearningHTTPcachinginGo" target="_blank" rel="noopener noreferrer">Learning HTTP caching in Go</a> 글을 기준으로 잡고 간단한 캐시 서버를 제작하게 되었습니다.</p><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>해당 글에서는 HTTP Cache와 관련된 Header에 대해서 자세히 설명하지 않으며 HTTP Cache와 관련된 Header가 어떻게 Request되고 Server는 어떤 식으로 처리되는지를 그에 대한 효율성을 중점적으로 서술합니다.</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="tldr">TL;DR<a class="hash-link" href="#tldr" title="제목으로 바로 가기">​</a></h2><ol><li>HTTP Cache Header는 HTTP로 통신하는 주체들이 캐시를 처리하기 위한 하나의 interface일 뿐 그것에 대한 처리 로직을 구현하는 것은 Client (browser) 와 Server (web server) 입니다.</li><li><code>Express.js</code> 니 <code>ASP.NET Core</code> 과 같은 상용화 된 웹 서버 제품을 사용하는 경우 캐시 관련 해더를 처리하는 부분은 블랙박스 영역이라고 생각하면 됩니다.</li></ol><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="요구사항">요구사항<a class="hash-link" href="#요구사항" title="제목으로 바로 가기">​</a></h2><ol><li>우선 캐시가 적용된 경우와 적용되지 않은 경우를 보기 쉽도록 이미지 파일을 읽어오는 API를 만들고 가장 궁금했던 <code>ETag</code> 처리 로직을 위해서 텍스트 파일을 읽어오는 API를 만들도록 합니다.</li><li>이미지 API의 경우 제일 단순하게 <code>Cache-Control</code> Header를 통해서 <code>max-age</code> 만을 가지고 캐시 적용 유무 차이를 볼 것이고</li><li>텍스트 API의 경우 파일이 수정될 때마다 <code>ETag</code> 값을 변경하여 Response 하는 로직과 함께 <code>Cache-Control</code> Header로 시간에 따른 캐시 변화도 볼 것입니다.</li></ol><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="코드를-보여주세요">코드를 보여주세요<a class="hash-link" href="#코드를-보여주세요" title="제목으로 바로 가기">​</a></h2><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>아래에 사용된 코드는 <a href="https://github.com/parkgang/http-cache-server-with-go" target="_blank" rel="noopener noreferrer">parkgang/http-cache-server-with-go</a> 에서 살펴보실 수 있습니다.</p></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="maingo">main.go<a class="hash-link" href="#maingo" title="제목으로 바로 가기">​</a></h3><ol><li>HTTP Cache를 이해하기 위한 간단한 서버라서 로직은 모두 <code>main.go</code> 에 몰아져있습니다.</li><li>endpoint는 <code>/image</code>, <code>/text</code> 2가지가 있고 위에서 말한 이미지와 텍스트를 조회하는 API 입니다.<ol><li>이미지의 경우 메모리 상에서 생성하고 응답할 것이며</li><li>텍스트의 경우 <code>assets</code> 디렉터리에 있는 <code>note.txt</code> 파일을 사용할 것입니다.</li></ol></li><li><code>Cache-Control</code> Header의 경우 이미지와 텍스트 모두 동일하게 <code>private, max-age=5</code> 가 적용됩니다.<ol><li>확실한 테스트를 위해 브라우저만 캐시되도록 <code>private</code> 을 지정하였고</li><li>캐시 적용 유무를 빠르게 보기 위하여 아주 짧은 시간인 <code>max-age</code> 를 5초로 지정하였습니다.</li></ol></li><li><code>ETag</code> 의 경우 만드는 방법이 여러가지 있는 것으로 알지만 가장 단순하게 파일이 수정된 일자를 MD5 해시 함수를 이용하여 생성하였습니다.<ol><li>이로써 파일이 수정될 때마다 (파일을 그냥 저장할 때도 해당) 수정된 일자가 변경되어서 <code>ETag</code> 가 계속 변하게 될 것입니다.</li></ol></li><li><code>r.Header.Get("If-None-Match")</code> 쪽 코드를 보면 <code>ETag</code> 를 검증하는 로직이 있는데 request header에 <code>If-None-Match</code> 값이 존재하고 request한 <code>ETag</code> 와 현재 파일의 <code>ETag</code> 를 구해서 비교 후 같으면 <code>304</code> 를 응답하는 코드입니다.<blockquote><p>물론 제품 수준의 퀄리티는 아니지만 메커니즘 이해를 위한 구현은 이렇게 쉬운데 이 부분이 블랙박스로 처리되어 참으로 이해가 어려웠습니다.</p></blockquote></li><li>또한, 각각의 API가 실제로 호출 여부를 확인하기 위해서 호출 시 응답 전에 console에 글자를 출력하도록 코딩되어 있습니다.</li></ol><div class="language-go codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">main.go</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-go codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">package</span><span class="token plain"> main</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"bytes"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"crypto/md5"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"flag"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"fmt"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"image"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"image/color"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"image/draw"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"image/jpeg"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"io/ioutil"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"log"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"net/http"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"os"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"strconv"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"strings"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> root </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> flag</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">String</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"root"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"./assets"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"file system path"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">func</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Handle</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">FileServer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Dir</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">*</span><span class="token plain">root</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">HandleFunc</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/image"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> imageHandler</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">HandleFunc</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/text"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> textHandler</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    log</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Listening on 8080"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">ListenAndServe</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">":8080"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        log</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Fatal</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"ListenAndServe:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">func</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">imageHandler</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">w http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ResponseWriter</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> r </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Request</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    m </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> image</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">NewRGBA</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">image</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Rect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">240</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">240</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 검은색 이미지 생성</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tempImage </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> color</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">RGBA</span><span class="token punctuation" style="color:#393A34">{</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">255</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    draw</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Draw</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">m</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> m</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Bounds</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">image</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Uniform</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">tempImage</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> image</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Point</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> draw</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Src</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// jpeg 형식으로 이미지를 인코딩하고 ResponseWriter에 writes 합니다.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> img image</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Image </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> m</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    buffer </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">new</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bytes</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Buffer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> jpeg</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Encode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">buffer</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> img</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        log</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"unable to encode image."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    log</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"이미지 응답"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    w</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Header</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Cache-Control"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"private, max-age=5"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    w</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Header</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Content-Type"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"image/jpeg"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    w</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Header</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Content-Length"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> strconv</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Itoa</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">buffer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Bytes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">_</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> w</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Write</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">buffer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Bytes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        log</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"unable to write image."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">func</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">textHandler</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">w http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ResponseWriter</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> r </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Request</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    filename </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"./assets/note.txt"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 마지막 수정 시간 가져오기</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    file</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Stat</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">filename</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        log</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    modifiedtime </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> file</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">ModTime</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    etag </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> fmt</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Sprintf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"%x"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> md5</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token function" style="color:#d73a49">byte</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">modifiedtime</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">String</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    w</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Header</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Etag"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> etag</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    w</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Header</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Cache-Control"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"private, max-age=5"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// etag가 변하지 않았다면 304 응답</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> match </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> r</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Header</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"If-None-Match"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> match </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> strings</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Contains</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">match</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> etag</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">            log</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"etag가 변하지 않았음으로 304 응답"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">            w</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WriteHeader</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">StatusNotModified</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 파일을 읽은 후 응답</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    dat</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> ioutil</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">ReadFile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">filename</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        log</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    log</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"파일 응답"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    fmt</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Fprint</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">w</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">string</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dat</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="assetsnotetxt">assets/note.txt<a class="hash-link" href="#assetsnotetxt" title="제목으로 바로 가기">​</a></h3><ol><li>텍스트 API에서 응답할 때 사용되는 텍스트 파일인데 일부로 <code>3MB</code> 으로 무겁개 해서 캐시 효과가 얼마나 큰지 알 수 있도록 디자인하였습니다.</li><li>또한, <code>304</code> 응답 시 얼마나 응답 바디가 작은지 확인할 수 있습니다.</li></ol><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="assetsindexhtml">assets/index.html<a class="hash-link" href="#assetsindexhtml" title="제목으로 바로 가기">​</a></h3><ol><li>Server API를 소비하는 Client 웹 페이지 입니다.</li><li><code>이미지 요청</code> 버튼을 누르면 이미지를 조회해서 보여주고</li><li><code>텍스트 요청</code> 버튼을 누르면 텍스트를 조회해서 보여주는 아주 간단한 웹 페이지 입니다.</li></ol><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>독자분들은 아시나요...? 정적 파일 요청을 브라우저의 URL에 직접 넣어서 요청하면 캐시 되지 않는다는 것을...🥲</p><p>분명 캐시가 적용된 URL인데 계속 요청을 해도 캐시되지 않고 새롭게 응답받길래 왜 그러지 하고 트러블 슈팅을 해보니 브라우저 URL에 직접 넣어서 호출해서 그렇더군요</p><p>그래서 아래와 같이 간단한 웹 페이지를 만들어서 호출할 수 있도록 디자인하였습니다.</p><p>에초에 GUI라서 확인하기도 쉬우실 꺼에요!</p></div></div><div class="language-html codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-html codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token doctype punctuation" style="color:#393A34;font-style:italic">&lt;!</span><span class="token doctype doctype-tag" style="color:#999988;font-style:italic">DOCTYPE</span><span class="token doctype" style="color:#999988;font-style:italic"> </span><span class="token doctype name" style="color:#999988;font-style:italic">html</span><span class="token doctype punctuation" style="color:#393A34;font-style:italic">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">html</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">lang</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">ko</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">meta</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">charset</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">UTF-8</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">meta</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">name</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">viewport</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">content</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">width=device-width, initial-scale=1.0</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">title</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">Document</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">title</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">style</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">      </span><span class="token style language-css selector class" style="color:#00009f">.text-area</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css property" style="color:#36acaa">display</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> inline-block</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css property" style="color:#36acaa">width</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">240</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css property" style="color:#36acaa">height</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">240</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css property" style="color:#36acaa">border</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">1</span><span class="token style language-css unit">px</span><span class="token style language-css"> solid </span><span class="token style language-css color">black</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css property" style="color:#36acaa">overflow</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> hidden</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">      </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">style</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">body</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag special-attr attr-name" style="color:#00a4db">onclick</span><span class="token tag special-attr attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag special-attr attr-value value javascript language-javascript function" style="color:#d73a49">handleGetImage</span><span class="token tag special-attr attr-value value javascript language-javascript punctuation" style="color:#393A34">(</span><span class="token tag special-attr attr-value value javascript language-javascript punctuation" style="color:#393A34">)</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">이미지 요청</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">br</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">img</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">image</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">src</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">alt</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">이미지가 표시 안됨</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">hr</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag special-attr attr-name" style="color:#00a4db">onclick</span><span class="token tag special-attr attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag special-attr attr-value value javascript language-javascript function" style="color:#d73a49">handleGetTxt</span><span class="token tag special-attr attr-value value javascript language-javascript punctuation" style="color:#393A34">(</span><span class="token tag special-attr attr-value value javascript language-javascript punctuation" style="color:#393A34">)</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">텍스트 요청</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">br</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">span</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">text-area</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">body</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">    </span><span class="token script language-javascript keyword" style="color:#00009f">async</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript function" style="color:#d73a49">handleGetImage</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">      </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> response </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">await</span><span class="token script language-javascript"> </span><span class="token script language-javascript function" style="color:#d73a49">fetch</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript template-string template-punctuation string" style="color:#e3116c">`</span><span class="token script language-javascript template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token script language-javascript template-string interpolation dom variable" style="color:#36acaa">window</span><span class="token script language-javascript template-string interpolation punctuation" style="color:#393A34">.</span><span class="token script language-javascript template-string interpolation property-access">location</span><span class="token script language-javascript template-string interpolation punctuation" style="color:#393A34">.</span><span class="token script language-javascript template-string interpolation property-access">origin</span><span class="token script language-javascript template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token script language-javascript template-string string" style="color:#e3116c">/image</span><span class="token script language-javascript template-string template-punctuation string" style="color:#e3116c">`</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">      </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">if</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">response</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">ok</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">        </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> imageBlob </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">await</span><span class="token script language-javascript"> response</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">blob</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">        </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> imageObjectURL </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript constant" style="color:#36acaa">URL</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">createObjectURL</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">imageBlob</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">        </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">getElementById</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">"image"</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">src</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> imageObjectURL</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">      </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">else</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">        </span><span class="token script language-javascript function" style="color:#d73a49">alert</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">"HTTP-Error: "</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">+</span><span class="token script language-javascript"> response</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">status</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">      </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">    </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">    </span><span class="token script language-javascript keyword" style="color:#00009f">async</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript function" style="color:#d73a49">handleGetTxt</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">      </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> response </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">await</span><span class="token script language-javascript"> </span><span class="token script language-javascript function" style="color:#d73a49">fetch</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript template-string template-punctuation string" style="color:#e3116c">`</span><span class="token script language-javascript template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token script language-javascript template-string interpolation dom variable" style="color:#36acaa">window</span><span class="token script language-javascript template-string interpolation punctuation" style="color:#393A34">.</span><span class="token script language-javascript template-string interpolation property-access">location</span><span class="token script language-javascript template-string interpolation punctuation" style="color:#393A34">.</span><span class="token script language-javascript template-string interpolation property-access">origin</span><span class="token script language-javascript template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token script language-javascript template-string string" style="color:#e3116c">/text</span><span class="token script language-javascript template-string template-punctuation string" style="color:#e3116c">`</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">      </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">if</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">response</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">ok</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">        </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> text </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">await</span><span class="token script language-javascript"> response</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">text</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">        </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">getElementsByClassName</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">"text-area"</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">[</span><span class="token script language-javascript number" style="color:#36acaa">0</span><span class="token script language-javascript punctuation" style="color:#393A34">]</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">innerHTML</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> text</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">      </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">else</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">        </span><span class="token script language-javascript function" style="color:#d73a49">alert</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">"HTTP-Error: "</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">+</span><span class="token script language-javascript"> response</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">status</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">      </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">    </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">html</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="원하는-대로-동작하는지-확인해보자">원하는 대로 동작하는지 확인해보자<a class="hash-link" href="#원하는-대로-동작하는지-확인해보자" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="서버-실행">서버 실행<a class="hash-link" href="#서버-실행" title="제목으로 바로 가기">​</a></h3><ol><li>자 이제 캐시가 잘 되는지 확인해볼까요? 위에서 말한 repo에서 소스코드를 clone 받고 <code>go run main.go</code> 으로 서버를 시작해 봅시다.</li><li>캐시 여부를 편리하기 보기위해서 저는 친숙한 <code>Chrome</code> 을 사용하도록 하겠습니다.<blockquote><p>대부분 비슷하긴 하지만 <code>Chrome</code> 이 아니여도 디버깅이 편하신 분은 다른 브라우저를 사용하셔도 됩니다.</p></blockquote></li><li>그리고 <a href="http://localhost:8080" target="_blank" rel="noopener noreferrer">http://localhost:8080</a> 주소로 접속하고 개발자 도구를 켜주세요! 아마 아래와 같이 화면이 될 것 입니다.<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAABDUlEQVQYlWPw8PNpCI+JfR4RFnnN1ML2ppqW/k1NHcOb2joGN/UNTK6ZWVg/NzW3bmU4furk3GcvX/z/9OHj//TMnP9qmnr/TS2s/xsamf43AGFDk/86ugZLGN68fjP9PwT8TEhI+isqJvlXWUXtr4KiCgj/VFBU+S+voDyX4dmzZzP+/PkDUvg7Lj7xn4io5D8lZbV/Cooq/+QVlH+DFMrJK81jePXq1TSoiT/i4hP/iIhK/FFT1/6jqa3/R0lZ7Ye8gjJI4RyG169fL4Aq/B8fn/RfVEzyv5Ky2n+QSTAsJ6+0nOHMmTOpd+/e3XHv3r1VQcEha4SFxdbIySutkZGVB+FVMrIK26Vl5LIALGmKgzQxTmQAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2546" height="1886"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.1a2fd40.2546.png" srcset="/assets/ideal-img/1.1a2fd40.2546.png 2546w" width="2546" height="1886"></noscript></div></div></li></ol><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="이미지-캐시-확인">이미지 캐시 확인<a class="hash-link" href="#이미지-캐시-확인" title="제목으로 바로 가기">​</a></h3><p>우선 <code>이미지 요청</code> 버튼을 클릭해 볼까요?</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAABG0lEQVQYlTXKvU7CUBiA4XMRotRGen56KrQQitKAabwARxYJDAgYFnd/JxdvQR3EACExId31Cjp1JilJZezUqQuE034GjW/ybg86azQe291e2Gq251b91Nd009dLFd8omn7ZrM5rdTu0avYTOq6fDLOyDNPJBK5vHoByHayaDeaRBWbFgnKlCkbJnCJN4y8IIRiNx+vbu/tEkuTEKJaTQsFI9IKx1vI6UMaHyHXd18ViAXEcb/qXg3QvK6cqz6eE8pRSviGUQ06h7yiKomf4a9Xp9MROJiso0wQmqlCIusJE3cI3FIbhSAjxKy+6fcjsSsDUQ9iC/3MK/UCe5w2CIPhaLr9n582WI+0fOJRpDsbMUTCbYcI+cwq5+gGYsn9T25cl4wAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2546" height="1886"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.1f5ae80.2546.png" srcset="/assets/ideal-img/2.1f5ae80.2546.png 2546w" width="2546" height="1886"></noscript></div></div><p>위와 같이 요청 후 서버에서 응답된 것을 확인할 수 있네요 응답 받은 이미지 용량은 <code>1.6kB</code> 이라고 하네요! 그럼 바로 한번 더 빠르게 <code>이미지 요청</code> 버튼을 클릭해 볼까요?</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAABIklEQVQYlSXKPU/CQByA8fsOvpLa3vWu1xYotBAqmMYv4MYiwUStJmjibEx8GVz4CuogpiEkJqS7fgImZpKSIGMnJhYI1/4N+iTP9kNH9frziX8RNxvNkVs7jIy8E+WsUpS37Mh2KqP9mhe7Va+NKrWDzq4kQa/bhdu7e6A8C27VA6fsgl1ywSm5YBWcHtI5f0UIQRAEy4fHp0SWcWI75aRgFZNiwV6a2TxoXO+gwWDwNh6PYT6fr1pX16m0p6SGmUs5N1JdN1eUcSCEfqDZbPYC/y1Oz3yxsbkjKNMFJppQCFsQVQMF03cUx3EghPiT5/4lbG1ngGkGqKoGa7QeY/qJhsNhazKZfE+nP/3jRjPMSHJImR4SwkJMWJ+o7EvB6s0v2vJ/u620BCkAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2546" height="1886"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.10668eb.2546.png" srcset="/assets/ideal-img/3.10668eb.2546.png 2546w" width="2546" height="1886"></noscript></div></div><p>이번에는 뭔가 요청이 된거 같으나 응답 부분의 용량이 <code>(디스크 캐시)</code> 인 것을 확인할 수 있습니다. 네, 그렇습니다 캐시되서 그렇습니다. 🤭 네트워크 요청 내역을 자세히 봐볼까요?</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAYAAAALpr0TAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA3ElEQVQYlVWQSW7EMAwE9RJbXrRYkrV4JDsLkCDnnPL/x1RgTyZIDsUGyGaDoGj1oNx2anu62I9XytZIpZJLZU2nNkQIEe9WQkj4kPEhsbiVxUUWf0dbj3C5MbuMDZmYCqncLtaYWdd015gR4e0T//FFPN55aZWWN2JIzMr+wSBkJ5FdjzMLz3VnCxGrLXKY6eX0i5CDopMTxnqOdlBiQStzDU/zA/HYPI35thPihtKOaTY/2HviWbp+vG7Z6nG9xfmEsQGlF7TxzGpBaOOutLN5pg+TZpw0w6j+8Q0x3YY6j7o07QAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1596" height="1430"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.f0bceb0.1596.png" srcset="/assets/ideal-img/4.f0bceb0.1596.png 1596w" width="1596" height="1430"></noscript></div></div><p>응답 header에 <code>Cache-Control: private, max-age=5</code> 가 존재하는 것을 볼 수 있습니다. 이미지 조회 API에 넣어 둔 header인 것을 알 수 있습니다. 앞으로 5초 동안은 해당 URL 요청에 대해서 Server에 물어보지 않고 브라우저 캐시를 사용합니다.</p><p>중요한 것은 네트워크 탭에는 <strong>마치 요청된 것</strong> 같아 보이지만 실제로는 <strong>Server에 요청하지 않는</strong> 다는 것 입니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAMklEQVQImWOITCz97xtd9d8/puK/b1TZfyf/7P+23tn/XT1D/we7G/3X1zf+r6xm+h8Al80TNRjIjQoAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1252" height="102"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.a96c270.1252.png" srcset="/assets/ideal-img/5.a96c270.1252.png 1252w" width="1252" height="102"></noscript></div></div><p>Server Console을 보니까 요청이 1회 들어온 것을 확인할 수 있습니다.</p><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>앗, 혹시라도 1회 이상 요청되었나요? 그렇다면 2가지를 의심하셔야 합니다.</p><p>첫번째로 브라우저 개발자 도구에 <code>캐시 사용 중지</code> 체크 박스가 해재되어있는지 확인해주세요. 해당 체크박스가 체크되어 있으면 캐시되지 않습니다ㅠㅠ</p><p>두번째로 <code>max-age</code> 값을 올리시거나 아니면 더 빨리 버튼을 클릭해 보세요 빠른 테스트를 위해서 캐시 시간을 5초로 지정하였는데 5초가 지나면 해당 URL의 캐시는 무효화 되어 Server에게 다시 요청을 보내게 됩니다. 해당 경우가 아닌지 한번 확인해보세요</p></div></div><p>이외 아래의 RGBA 값과 <code>max-age</code> 값을 바꾸면서 테스트 해보세요. 첫 조회된 <code>max-age</code> 시간 동안은 색상 값을 변경하더라도 조회되지 않지만 <code>max-age</code> 가 지나면 변경되는 것을 확인할 수 있습니다.</p><div class="language-go codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">main.go</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-go codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">func</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">imageHandler</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">w http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ResponseWriter</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> r </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Request</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 검은색 이미지 생성</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    tempImage </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> color</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">RGBA</span><span class="token punctuation" style="color:#393A34">{</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">255</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    w</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Header</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Cache-Control"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"private, max-age=5"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="텍스트-캐시-확인">텍스트 캐시 확인<a class="hash-link" href="#텍스트-캐시-확인" title="제목으로 바로 가기">​</a></h3><div class="admonition admonition-caution alert alert--warning"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>caution</h5></div><div class="admonition-content"><p>텍스트 확인 전 혹시 모르니까 캐시를 모두 날리면서 테스트 하세요. 개발자 도구를 활성화한 상태에서 새로고침 버튼에서 오른쪽 마우스를 클릭하면 아래와 같이 강력 세로고침 매뉴을 볼 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAABDUlEQVQYlTXMvU6DUBjG8XMPLcRGyjlwCA0BUiDUGjs76ORH55L6NXgVLt6COlhDjIkJwVln05G5CQOMNIG6twHOa4j1SX7bPw86PD66sx13SQhdKKoWa6YTa4YV64YV9y13sbc/Wg6Go3v0PZ/PPoIAXn0fTk7HoJsOuMMDsJ0BWA3bBcO03tDPavUIf9tcXt3UHN+pqdKriaQ0NkRSABM6Q1mWPZVlCYyxcuJNWZvrMJmqjEgKw4SWTShi+QXlef6wfVxPvGnV5nYqtadXmt6vZKquMaFN+IyKovC3IXjeBXB8B2SqQvP0T8TyO4qi6DpJkq80TYOz83HYavGhiOVQ6OJGIHTJ564g3v4C8AGHQU1Q5vAAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2546" height="1886"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.93bb876.2546.png" srcset="/assets/ideal-img/6.93bb876.2546.png 2546w" width="2546" height="1886"></noscript></div></div></div></div><p>자 이제 <code>Etag</code> 가 있는 텍스트 입니다. <code>텍스트 요청</code> 버튼을 클릭해 볼까요?</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAABFUlEQVQYlWPwCvBriIpPeB4ZHnXN1Nz2ppqW/k0tHYOb2joGN/UNTK6ZWVg/NzW3bmXYuWf33Gs3b/x///bt/4ys3P9qWvr/TS1s/hsamf43AGFDk/86ugZLGPbt2zf92NGj/9+8efMzIzP7r4SU7F8tbb2/mhpafzU1dX6qaOv/V1JSmctw8eLFGQ8ePPj/9evX3wmJyf9ERCX+Kauo/1NUVAHh3wqKKv/l5JXmMbx582bafwj4ERef+EdUTPKPsor6HwVFFRD+AVU4h+HVq1cLoAr/x8cn/RcVk/yvrKL+H6QAhuXklZYznDlzJvXu3bs77t27tyosPHKNlLTsGiVltTUKCspr5BWUVykoKm+Xk1fMAgC6d4ji6g2T7wAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2546" height="1886"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.815b419.2546.png" srcset="/assets/ideal-img/7.815b419.2546.png 2546w" width="2546" height="1886"></noscript></div></div><p>위와 같이 <code>3.1MB</code> 으로 묵직한 텍스트가 응답되었습니다. 매 요청마다 <code>3.1MB</code> 을 다운로드 하면 모바일 환경에서 데이터는 남아나지 않을 것 입니다. 바로 또 다시 <code>텍스트 요청</code> 버튼을 클릭해 볼까요?</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAABHElEQVQYlWPwCvBriIpPeB4ZHnXN1Nz2ppqW/k0tXcOb2jr6N/UNTa6ZWVg/NzW3bmXYuWf33Gs3b/x/9/bt/4zM3P9qmrr/Tc2t/xsamf3XNzT5b2Bo+l9H12AJw759+6YfO3r0/5s3b35mZmX/lZSU/qujq/9XW0v3r5aWzk81Da3/KqoacxkuXrw448GDB/+/fv36OzEp5Z+IqMQ/FVWNf8rKqiD8W0FJ5b+cgvI8hjdv3kz7DwE/4uIT/4iISf5RUlb7o6Co8kdeUeWHgqLKfzl5pTkMr169WgBV+D8+Pum/qJjkf2UV9f8gBTAsL6+0nOHMmTOpd+/e3XHv3r1VYeGRa6Sk5dYoKautkVdQBuFVCorK2+XkFbMAyamI/CJ6d6oAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2546" height="1886"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.ef16521.2546.png" srcset="/assets/ideal-img/8.ef16521.2546.png 2546w" width="2546" height="1886"></noscript></div></div><p>텍스트 파일도 이미지 파일과 동일하게 <code>Cache-Control: private, max-age=5</code> 가 설정되어 있기 때문에 5초 동안 캐시되어 있는 것을 확인할 수 있습니다. 그럼 이번에는 5초 기달렸다가 요청을 보내볼 까요?</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAABG0lEQVQYlS3KT0vCYBzA8edF5GAKPs/27I/sT3NgKYzeQIfolOhBzZDKehFdegvVIaNDEMjuFcVudtplF2mHjd2EMek0UJz+YtgXPrcvOjw+uml3e7NWsz3daxz4klL1Fd30Va3qG2ZtWm9Ys/26dYs+vj5HU/8HfudzOL+4BrGiQ61ugWHWwKhuqZrxghzHuf+eTCBJkuXl8GrNFktrfddca6qeW0oVBSgVR8jzvIcoiiBN01X/bLBhGHYjycpGFOXciqcSEMI/oSRJ7mDbotM5zXYKbMZTKcNEyDChC8IJUMb8I4rj+Pl/hG6vDwWGBSrIwHEC5FMOY/4Vua47CILgPQzD8UmzZRdLZZunkk0ItTGhY8LRtzLmhn/TFIkRG1+MxwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2546" height="1886"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9.2161684.2546.png" srcset="/assets/ideal-img/9.2161684.2546.png 2546w" width="2546" height="1886"></noscript></div></div><p>이번에는 <code>141B</code> 로 매우 가벼운 응답이 온 것을 확인할 수 있습니다.</p><p>이유는 파일이 변경되지 않았기 때문에 Server에서 <code>ETag</code> 가 변경되지 않아 이전과 <code>ETag</code> 가 동일하기 때문에 <code>304</code> 를 응답하기 때문입니다.</p><p>덕분에 캐시가 끝나서 다시 <code>3.1MB</code> 응답받을 뻔! 했지만? Server에서 변경되지 않았다고 <code>304</code> 상태 코드와 <code>ETag</code> 를 header 응답해줘서 5초 동안은 캐시될 수 있도록 생명연장이 되었습니다. 😤 😮‍💨</p><p>그러면 궁금한 것이 이전 <code>ETag</code> 를 Server는 어떻게 알고 있는 걸까요?</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAYAAAALpr0TAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA40lEQVQYlT2P246EIBBE/ZDxiogICKiMOpN92WT//5POBtedh5N0OtVdVUXaduK6s6XMQdpfhCXh48bsF3xI+LBSWDtjjMNaj5sjxvoL6wLTvZPKUGifEDogR8eoLePkkGpC9IpODBd5Lvr9h8fyzRBfxLjhY8K4QNP2tJ38UNRlTV1WKKVZ08lkPMNori9ZXDfiT9i0krrpkVJzFYsbwS/Md94cQw46C3uqRjAqzdd+8n4eJB8JxjFkkZpu60ZQVS1CjqzPE20Dj6qlrLvL9p8iX+VMufG6HYRcxgZ0bj/oj/Uva+iGQ0I8cWcAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1594" height="1428"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/10.fda71f8.1594.png" srcset="/assets/ideal-img/10.fda71f8.1594.png 1594w" width="1594" height="1428"></noscript></div></div><p>진실은 Client (브라우저) 가 알아서 포함해서 보내기 때문입니다.</p><p>텍스트도 이미지와 같이 <code>max-age</code> 값을 바꾸거나 텍스트 파일을 수정해보면서 어떤 식으로 돌아가는지 확인해보세요. <code>ETag</code> 가 없을 때 캐시가 만료되면 어떻게 동작하는지 직접 확인하면 이해가 빠를 것 입니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>만들고나니 엄청나게 특별한 로직이 존재하지는 않았습니다. 그래도 실제로 캐시 유무에 따른 변화와 함께 상용으로 사용되는 웹 서버도 이와 비슷한 메커니즘으로 처리되겠구나 하는 것을 인지할 수 있었습니다.</p><p>덕분에 앞으로 웹 앱을 배포하고 무작정 캐시 문제라고 생각할 것이 아니라 논리적으로 트러블 슈팅을 할 수 있는 귀중한 경험치가 될 거 같습니다.</p><p>저와 같이 블랙박스 영역의 웹 서버 로직이 궁금하셨을 분들에게 조금이라도 도움이 됬으면 하며 저는 이만 물러가도록 하겠습니다. 읽어주셔서 감사합니다. 😀</p>]]></content>
        <category label="golang" term="golang"/>
        <category label="HTTP" term="HTTP"/>
        <category label="cache" term="cache"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Gin과 함께하는 React boilerplate를 공유합니다]]></title>
        <id>/2021/09/13/share-react-boilerplate-with-gin</id>
        <link href="https://parkgang.github.io/blog/2021/09/13/share-react-boilerplate-with-gin"/>
        <updated>2021-09-13T04:09:19.000Z</updated>
        <summary type="html"><![CDATA[Gin 을 사용하면서 손쉽게 config 변경도 가능하고 CRA 를 사용할 수 있는 boilerplate가 어디 없나? 그래서 만들어봤습니다.]]></summary>
        <content type="html"><![CDATA[<p><code>Gin</code> 을 사용하면서 손쉽게 config 변경도 가능하고 <code>CRA</code> 를 사용할 수 있는 boilerplate가 어디 없나? 그래서 만들어봤습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="tldr">TL;DR<a class="hash-link" href="#tldr" title="제목으로 바로 가기">​</a></h2><ol><li>gin + react boilerplate 이며 <a href="https://github.com/parkgang/react-with-gin-boilerplate" target="_blank" rel="noopener noreferrer">parkgang/react-with-gin-boilerplate</a> 에서 살펴보실 수 있습니다.</li><li>prod 환경도 json 가져와서 사용하는데 이는 보안에 위험할 수 있으니 기호에 맞게 수정해서 사용해주세요.<blockquote><p>예를 들어 환경변수로 바꾸기</p></blockquote></li></ol><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="왜-만든건가요">왜 만든건가요?<a class="hash-link" href="#왜-만든건가요" title="제목으로 바로 가기">​</a></h2><p>그동안 golang으로 그렇다 할만한 제품이 없어서 개인 프로젝트로 제품을 하나 제작해보려고 했습니다.</p><p>여러 golang의 웹 프레임워크 중 이전부터 많이 찾아보기도 헀고 지켜보고 있던 <code>gin</code> 으로 정하고 개발을 하려고 딱 준비를 했더니 신기한 점을 발견할 수 있었습니다.</p><p>뭐 라이브러리나 프레임워크가 얼마나 제공해주느냐에 따라 다르겠지만 기본적으로 개발 환경에 맞는 <code>config</code> 값을 주입받을 수 있는 기능과 같이 기본적인 기능이 <code>gin</code> 에 있을 줄 알았지만 없었습니다.</p><p>물론 <code>gin</code> 자체가 마이크로 웹 프레임워크 이므로 웹 프레임워크게 딱 필요한 기능있는 것이 맞습니다.</p><p>하지만 결국에 제품 개발을 하면서 환경분리 라던지~ 문서화 도구라던지~ 웹 프레임워크에 점점 살이 붙어져 나갈 텐데 이걸 일일이 프로젝트 마다 셋팅하는 것은 비효율적이라고 생각했습니다.</p><p>더구나 필자는 최근에 Server Side는 <code>ASP.NET Core</code> Client Side는 <code>CRA</code> 을 주로 개발해 왔었고 이외도 Server Side는 <code>express.js</code>, <code>nest.js</code> Client Side는 <code>next.js</code> 으로도 간단하게나 개발 경험이 있었는데 <code>ASP.NET Core</code> 같은 것만을 봐도 없는 기능이 무엇인 가 싶을정도로 많은 것을 지원합니다.</p><blockquote><p>특히 <code>config</code> 부분을 기본적으로 제공하는 기능이 마음에 들었습니다.</p></blockquote><blockquote><p>너무 익숙함에 속아서 그런지 <code>gin</code> 에 살짝 당황했었던... 🥲</p></blockquote><p>그래서 이참에 높은 이해도를 가지고 프로젝트를 시작하고 싶어 <code>gin</code> 에다가 <code>cra</code> 환경의 react를 사용하는 boilerplate를 만들기로 결심했고 미숙하지만 만들어진 결과물을 공유하려고 합니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="어떤-것을-중점으로-만들-것인가">어떤 것을 중점으로 만들 것인가?<a class="hash-link" href="#어떤-것을-중점으로-만들-것인가" title="제목으로 바로 가기">​</a></h2><p>당연히 기본은 gin server에서 react app 개발이 잘 이뤄지는 것이지만 그래도 저는 프로젝트 스트럭처에 관심많고 명확하고 깔끔 하이어라키를 디자인하고 싶어서 당연한 기본적인 목표 이외 아래의 목표를 추가로 설정하였습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="엄격한-타입-정의">엄격한 타입 정의<a class="hash-link" href="#엄격한-타입-정의" title="제목으로 바로 가기">​</a></h3><p>ts의 유틸리티 타입을 적극 사용해서 우아한 타입을 선언</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="모호한-문법말고-명확하고-일관성-있게">모호한 문법말고 명확하고 일관성 있게<a class="hash-link" href="#모호한-문법말고-명확하고-일관성-있게" title="제목으로 바로 가기">​</a></h3><p>특히 js/ts에서 이런 고민이 많이 발생하는데 예를 들어 어떤 경우에 익명함수를 사용할지 화살표 함수를 사용할 지, 모듈에서 <code>default</code> 으로 export 되는 친구는 변수명 바로 옆에 <code>export default</code> 으로 선언해줄지 해당 파일의 가장 마지막 줄에 선언할 지, 컴포넌트마다 디렉터리를 만들어서 <code>index.ts</code> 으로 export 해주는 전략을 사용할지 등 꽤나 고민되는 요소가 많이 있습니다.</p><p>필자는 이런 부분의 규칙을 만드는 것에 매우 관심이 많음으로 이 부분을 감안하여 일관성있는 boilerplate를 만들고 싶었습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="server-side-api-문서화-및-http-status-code-명확하게-디자인">Server Side API 문서화 및 Http Status Code 명확하게 디자인<a class="hash-link" href="#server-side-api-문서화-및-http-status-code-명확하게-디자인" title="제목으로 바로 가기">​</a></h3><p>요청 성공하면 대충 <code>200</code> 으로 response 하는 것이 아닌 각각의 요청 처리에 맞는 Http Status Code 적절히 사용하고 싶었습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="recoil-react-query를-이용하여-react-명확하게-상태관리하기">recoil, react-query를 이용하여 react 명확하게 상태관리하기<a class="hash-link" href="#recoil-react-query를-이용하여-react-명확하게-상태관리하기" title="제목으로 바로 가기">​</a></h3><p><code>recoil</code> 과 <code>react-query</code> 모두 아주 대중적으로 사용되는 라이브러리 아닙니다.</p><blockquote><p>물론 <code>redux</code>, <code>context api</code> 친구와 비교한다면 이며 기준은 상대적입니다.</p></blockquote><p>필자는 해당 라이브러리를 너무 좋아한다 그래서 이번 boilerplate에 예제 코드를 넣어서 혹시라도 다른 분들께 insight를 전달할 수 있지 아늘까라는 기대로 넣으려고 했습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="예외처리">예외처리<a class="hash-link" href="#예외처리" title="제목으로 바로 가기">​</a></h3><p>제품 레벨으로써는 너무나도 당연한 것이지만 boilerplate으로 써 앱이 돌아갈 수 있는 환경까지 이면 되는데 여기에 예외처리까지 잘된 코드를 넣고 싶었습니다.</p><blockquote><p>문서화는 덤 ✌️</p></blockquote><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="microsoft-teams-ui-component">Microsoft Teams UI Component<a class="hash-link" href="#microsoft-teams-ui-component" title="제목으로 바로 가기">​</a></h3><p>react에 많은 component framework가 있지만 그 중에서도 아주 외소한 <code>@fluentui/react-northstar</code> 를 사용해서 Teams 룩앤필이 된 예제코드를 넣고 싶었습니다.</p><p>직장내에서 매신저 앱으로 slack 혹은 teams 혹은 회사마다 다른 사내매신저를 사용하실 텐데 teams를 사용하시는 곳이라면 해당 boilerplate를 보고 오 teams app이다 라는 반가움을 전달하고 싶었습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="디버깅-환경">디버깅 환경<a class="hash-link" href="#디버깅-환경" title="제목으로 바로 가기">​</a></h3><p>프로젝트는 쉽게 실행되어 디버깅에 불편함이 없어야 개발하면서 계속 즐거움이 유지된다고 생각합니다. 이를 꼭 만족시키기 위해 좋은 디버깅 설정 rnd 했습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="제작-완료-회고-및-마무리">제작 완료 회고 및 마무리<a class="hash-link" href="#제작-완료-회고-및-마무리" title="제목으로 바로 가기">​</a></h2><p>이렇게 위와 같은 목표로 boilerplate가 완성되었습니다.</p><p><a href="https://github.com/parkgang/react-with-gin-boilerplate" target="_blank" rel="noopener noreferrer">parkgang/react-with-gin-boilerplate</a> 에서 만나보실 수 있으며 기존 예제 코드가 마음에 안드는 경우</p><blockquote><p>예를 들어 mysql을 사용하지 않는 다거나 그냥 바닐라 cra을 원하는 경우</p></blockquote><p>기호에 맞게 수정해서 사용하시면 됩니다.</p><p>이외 개발하면서 생각보다 많은 지식을 필요로 하는 부분은 SPA 이였습니다. 번들링된 정적파일 서빙 부터 CSR Router까지 Front End의 기본을 잘 이해하고 접근해야하는 부분이였는데 상당히 재미있었습니다.</p><p>끝으로 부족한 실력으로 boilerplate를 만들어 보았는데 사용하시는 분이 계실지 모르겠네요. 혹시라도 사용하시게 된다면 티를 내주시면 감사하겠습니다! 그러면 많은 동기부여가 될꺼 같아요! 읽어주셔서 감사합니다.</p>]]></content>
        <category label="promotion" term="promotion"/>
        <category label="boilerplate" term="boilerplate"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Next.js의 SSG 제대로 이해하고 사용하기]]></title>
        <id>/2021/09/07/lets-properly-understand-and-use-the-ssg-of-nextjs</id>
        <link href="https://parkgang.github.io/blog/2021/09/07/lets-properly-understand-and-use-the-ssg-of-nextjs"/>
        <updated>2021-09-06T15:09:55.000Z</updated>
        <summary type="html"><![CDATA[next.js의 장점 중 하나로 SSR 을 지원하는 것 인데 어떻게 전역 상태를 가지고 있는 걸까? PHP, JSP와 같은 SSR과 다른 걸까?]]></summary>
        <content type="html"><![CDATA[<p>next.js의 장점 중 하나로 <code>SSR</code> 을 지원하는 것 인데 어떻게 전역 상태를 가지고 있는 걸까? PHP, JSP와 같은 SSR과 다른 걸까?</p><p>내가 아는 SSR은 Server Side에서 각 페이지 별로 html을 생성해서 Response (혹은 파일 서빙) 을 해주는 것인데 next.js를 공부하고 사용해보면서 이부분에서 아직 찝찝한 부분이 있어서 next.js도 그렇게 동작하는게 맞는가? 에 대한 의문이 항상있었습니다.</p><blockquote><p>물론 SSR 단어 뜻 그대로 서버 측에서 렌더링 한다라는 뜻을 의미하지만 필자는 계속 JSP 같은 전통적인 SSR와 엄청 햇갈렸습니다.</p></blockquote><p>그동안 CRA 환경 즉, SPA에서 개발을 하고 있었는데 갑자기 next.js의 원리에 대해 궁금해서 이참에 다시 정리를 해보면서 얻은 경험치에 대해 공유해보려고 합니다.</p><p>그럼 SPA를 가공할 수 있는 여러 형태라고 할 수 있는 CSR, SSG, SSR를 비교하면서 설명해보도록 하겠습니다.</p><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>추가적으로 해당 글에서는 SPA, CSR, SSG, SSR과 같은 단어 설명과 next.js에 대한 설명을 포함하고 있지 않습니다.</p></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="tldr">TL;DR<a class="hash-link" href="#tldr" title="제목으로 바로 가기">​</a></h3><ol><li>next.js에서 말하는 SSR도 사실 하나 뿐인 SPA으로 동작합니다.</li><li>다만, empty html의 SPA 형태가 아닌 Server Side에서 미리 가공된 html을 가지고 SPA으로 동작합니다.</li></ol><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="내가-알고-있던-ssr은">내가 알고 있던 SSR은?<a class="hash-link" href="#내가-알고-있던-ssr은" title="제목으로 바로 가기">​</a></h2><p>필자는 SSR를 들었을 때 가장 처음으로 떠오른 것은 jsp 이였습니다.</p><p>Client가 요청한 페이지를 Server Side에서 열심히 가공해서 Response 하는 것을 말이죠</p><p>이는 주로 SSR를 설명할 때 가장 많이 소개되는 시나리오로 다른 페이지로 넘어갈 때 페이지의 내용을 새로 가공해서 넘겨주기 때문에 <strong>화면이 깜빡</strong> 한다 라고 하는 그 메커니즘을 가진 친구입니다.</p><p>근데 next.js 해보면 SSR임에 불구하고 아주 부드럽게 CSR처럼 화면 전환이 이뤄지는 것을 볼 수 있습니다.</p><blockquote><q>어라...? 내가 뭔가 잘못 이해했구나...</q></blockquote><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="그런데-어떻게-전역-상태가-관리되는-거지">그런데 어떻게 전역 상태가 관리되는 거지?<a class="hash-link" href="#그런데-어떻게-전역-상태가-관리되는-거지" title="제목으로 바로 가기">​</a></h3><p>이뿐만이 아니라 위에 내가 알고 있는 SSR을 대입하면 전역 상태는 어떻게 관리되는 건지 의문이 생깁니다.</p><p>이동하는 매 페이지는 다른 html인데 전역 상태가 불가능 할 것입니다.<br>
<!-- -->하지만 next.js는 SSR임에 불구하고 잘 처리됩니다.</p><p>사실 next.js only SSR이 아닌 CSR과 SSR의 장점을 섞은 방식인 것을 알 수 있습니다.<br>
<!-- -->그럼에도 불구하고 SSR 단어는 계속 이전 개념과 충돌하게 되었습니다.</p><p>혹시라도 저와 같으신 분이 있다면 해당 글로 이해가 풀리셨으면 좋겠습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="그럼-nextjs의-ssr은-어떻게-작동하는거야">그럼 next.js의 SSR은 어떻게 작동하는거야?<a class="hash-link" href="#그럼-nextjs의-ssr은-어떻게-작동하는거야" title="제목으로 바로 가기">​</a></h2><p>가장 햇갈리고 오해하기 쉬운 것이 SSG, SSR 개념이 들어가니 모든 페이지가 정적으로 구어져있고 페이지 이동마다 Server Side에서 구어진 파일을 응답해서 탐색하겟지? 일 수 있습니다.</p><p>하지만 아닙니다! <ins><strong>첫 요청하는 페이지에 대해서만</strong></ins> SSG 혹은 SSR으로 Server Side에서 미리 구어 놓은 파일을 응답하는 것이고 그 이후부터는 router를 통해 이동하는 것은 CSR 입니다.</p><p>사실 next.js에서 말하는 SSG와 SSR은 Server Side에서 build 시점에 구어 줄까?<sup>SSG</sup> 아님 요청마다 구어 줄까?<sup>SSR</sup> 라고 봐도 무방합니다.</p><p>결국에는 첫 요청하는 페이지만 Server Side에서 구어진 파일을 사용하고 이후 부터는 CSR입니다.</p><p>그렇기 때문에 전역 상태와 같은 것이 가능한 것입니다. 첫 요청한 페이지를 SPA 삼아서 CSR를 하기 때문이죠 마치 다른 페이지로 이동하면 다른 구어진 파일을 사용했구나 싶을 수 있지만 사실 CSR으로 이동 중 인 것입니다.</p><blockquote><p>물론 이동한 경로에서 새로고침을 하면 해당 URL 기준으로 다시 구어진 파일을 받겠지만요</p></blockquote><p>그래서 next.js의 SSR를 설명할 때 SSG와 SSR를 빼고 설명하기가 참 어려운 거 같습니다.</p><p>이 때문에 next.js에서 말하는 SSR을 말 그대로 Server Side에서 처리하는 렌더링이라고 생각하시는게 이해에 좋습니다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>참고로 next.js의 Pre Rendering 시스템은 기본적으로 SSG으로 동작하며 Server Side에서 build시 기본 파일을 구어내고 매 요청마다 재사용하게 됩니다.</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="serverless">serverless<a class="hash-link" href="#serverless" title="제목으로 바로 가기">​</a></h2><p>Static으로 구어지기 때문에 next.js server 이외 serverless deployment가 동작할 수 있는 것을 생각할 수 있습니다.</p><p>사실 이 부분이 궁금했습니다. 이걸 잘 응용하면 굳이 정적 사이트 생성을 위해서 <code>Gatsby.js</code> 을 사용하지 않을 수 있을까라는 생각으로 말이죠</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="한번-해보자">한번 해보자<a class="hash-link" href="#한번-해보자" title="제목으로 바로 가기">​</a></h3><p>실습을 위해서 <a href="https://github.com/parkgang/next.js-ssg-tutorial" target="_blank" rel="noopener noreferrer">parkgang/next.js-ssg-tutorial</a> 코드를 참고하시면 됩니다.</p><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>코드 상태는 정말 안 좋으므로 돌아가는 샘플로서만 사용해 주세요.</p><blockquote><p>해당 글 샘플을 위해서 태어난 코드라 상태가 🤭</p></blockquote></div></div><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>해당 글을 위해서 따로 소스 코드를 생성할 까 싶었지만 사전에 미리 SSG를 테스트 하면서 이용 된 <a href="https://docs.microsoft.com/ko-kr/azure/static-web-apps/deploy-nextjs" target="_blank" rel="noopener noreferrer">정적으로 렌더링된 Next.js 웹 사이트 배포</a> 템플릿이 글에 사용할 수 있어서 해당 템플릿 코드를 수정해서 사용했습니다.</p></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="export">export<a class="hash-link" href="#export" title="제목으로 바로 가기">​</a></h3><p>제가 미리 <code>export</code> 명령어를 작성해 놓았습니다.</p><p><code>next</code> 디렉터리에서 <code>npm run export</code> 를 실행하여 <code>express/public</code> 으로 Static Html으로 export 되는지 확인합니다.</p><blockquote><p>굳이 <code>express/public</code> 으로 export 하는 이유는 이따가 설명드릴께요.</p></blockquote><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="왜-파일시스템에서는-안되는-걸까">왜 파일시스템에서는 안되는 걸까?<a class="hash-link" href="#왜-파일시스템에서는-안되는-걸까" title="제목으로 바로 가기">​</a></h3><p>여기서 궁금증이 생겻습니다. Static Html 인데 파일 시스템에서 router 동작이 어떻게 될까요?</p><p><code>express/public</code> 에 있는 파일은 serverless 환경에서 돌릴 수 있는 순수한 Static Html 입니다.</p><p>한번 웹 브라우저에서 해당 파일 경로로 파일을 열어볼까요?</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA50lEQVQYlWMwsTDJsrCxOWRmZrXZ2MRim76R2TZ9I9Nthkam2/T0jTebm1scNDczLmBYs3nL/L2Hj/w/euTo/4sXL/+/cPHK//MXL/2/ePHS/wsXLv6/cuXK/3379q5gOH/n+bS3P/6DAIj8g4bBMm/fvp3HsPLwrRmXn335////v99//vz99/cvAv/58+c3SOGLFy8WMOy9/Gjajddgjd///vv3+x8qBku8fv16HsPBC/cWPnjzDWz3v39gCg7+QQVev369iiG9e1HApNW7J129eL791OkznWfOoOD2c+fOTdy/f38oACkD4PoPFENLAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2588" height="1808"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.925c0f2.2588.png" srcset="/assets/ideal-img/1.925c0f2.2588.png 2588w" width="2588" height="1808"></noscript></div></div><p>짠, 뭔가 나오긴 하는데 대충 봐도 css가 모두 깨져있는거 같습니다. 원래 해당 앱은 아래와 같이 렌더링 되어야 하거든요</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAABFUlEQVQYlR3NvU7CUACG4XM5GqqRKAR10Ttw8SacvAvSUQfihLoZV5IuxRSVUvtjbIokTCYEA/QvxRba0nPaz5ThTZ7tJUfHtavTs/O3Wv2kw3FVYYerCrvcgVCp7AvcXrVTqzdeG/XDa9Lt9e6Ho29YXyYURcOgL0Puv0P5UGGZJkbDIURRfCLpKmotN0BIkQJgn7OYWXbCSjtxnnopEAV+m/iuczcL1lgs1xTIix8vLn6DpCg9cUO6WMZw7fkDmc3nLRQ5GKNJlmW0YIzmjG7zwjj1owSeY7eJ6zqPG1ZgQ8sbMPUjTN2/rV/GNrpjB9kqeCaSJF0Yhn6raVpT1w1eHij8QFF4wzB4VVWbuq7fSJJ0+Q9qjN++1KqBDwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2588" height="1808"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.3e41a79.2588.png" srcset="/assets/ideal-img/2.3e41a79.2588.png 2588w" width="2588" height="1808"></noscript></div></div><p>이외에도 link인 파란색 글자를 클릭해보면 아래와 같은 화면이 나오면서 정상동작 하지 않는 것을 확인할 수 있습니다. url 경로를 보더라도 에초에 존재할 수 없는 경로로 요청을 보내는 것도 확인할 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA10lEQVQYlXWMO0oEQRCG+xrz6K7u6vf0sPiYRUSYwBOYeImN9hZiqMFi5CMT04XJ9VIOzDAlrRstGHz8VfV/FDu7ON9c9v3X9VW/X7Ung/HtYEMafEhDTKt9160/193plmkfX1zTUgyJrG0IbUPaRbK/NORDIufCO+Nc7rhQZFwcfUgzop0F4AxS5xwFINVcPjMB+CSVIdRu8qFdULsFpF6kMpkpdwLwlQlQu7xIZb5B6imThcM8/okqf1RvB/FfBOAHK4r6pqz4Y1nxu7Li90fk20NR1Lc/djxWfh9Ip40AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2588" height="1808"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.ef0a323.2588.png" srcset="/assets/ideal-img/3.ef0a323.2588.png 2588w" width="2588" height="1808"></noscript></div></div><p>이에 대한 이유는 export된 파일의 <code>index.html</code> 을 열어보면 알 수 있습니다. 아마 대충 아래와 같은 형태를 가지고 있을 것입니다.</p><div class="language-html codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">index.html</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-html codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token doctype punctuation" style="color:#393A34;font-style:italic">&lt;!</span><span class="token doctype doctype-tag" style="color:#999988;font-style:italic">DOCTYPE</span><span class="token doctype" style="color:#999988;font-style:italic"> </span><span class="token doctype name" style="color:#999988;font-style:italic">html</span><span class="token doctype punctuation" style="color:#393A34;font-style:italic">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">html</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">lang</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">en</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">link</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">rel</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">stylesheet</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">data-href</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">https://fonts.googleapis.com/css?family=PT+Sans:300,400,700,800</span><span class="token tag attr-value entity named-entity" style="color:#36acaa">&amp;amp;</span><span class="token tag attr-value" style="color:#e3116c">display=optional</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">link</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">rel</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">preconnect</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">href</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">https://fonts.gstatic.com</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">crossorigin</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">meta</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">charset</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">utf-8</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">meta</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">name</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">viewport</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">content</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">width=device-width</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">meta</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">name</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">next-head-count</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">content</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">2</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">link</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">rel</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">preload</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">href</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">/_next/static/css/8964a576241b890e8a20.css</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">as</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">style</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">link</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">rel</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">stylesheet</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">href</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">/_next/static/css/8964a576241b890e8a20.css</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">data-n-g</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">noscript</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">data-n-css</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">noscript</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">script</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">defer</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">nomodule</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">src</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">/_next/static/chunks/polyfills-a40ef1678bae11e696dba45124eadd70.js</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token script"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">html</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>보니까 각종 css 및 script에 대한 정보를 <code>/</code> 경로로 요청하는 것을 볼 수 있습니다.</p><p>당연히 <code>/</code> 하위 경로에 해당 파일이 없을 테니 css가 깨지는 것은 당연합니다.<br>
<!-- -->한번 <code>index.html</code> 만 <code>/</code> 으로 되어 있는 부분을 모두 <code>./</code> 으로 변경하고 다시 열어볼까요?</p><div class="admonition admonition-tip alert alert--success"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</h5></div><div class="admonition-content"><p>Editor에서 <code>/_next</code> =&gt; <code>./_next</code> 으로 찾아서 변경하면 편리합니다.</p></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAABEUlEQVQYlR3NvU7CUACG4XNFDGILqMFbcPEmnLwL0lEH4oS6GVeSLmCKJrS25wxgJWEyMSiF/qS1pbT0nPYzMLzJs71EOmlctdrnb+3WWf9UaqpyvaE2jmW1VZfUWu2oL8nN16ZcvybD0ejentn4nExgmQymruNdH8OyKD6mE8xsG4PB4Inkm6Qb7YCYIwcg2G8qpqtM7O2mZe7nQBIGPRJ47t0yTLGKUg6U1Ze/rX7CrNr724v5KtrCWzsPZOk4XVQlhOBZURS8EoKXgh/y420eJBl8d90jnuc+7kSFHd/fgEWQYOH9HfwyX2M4d1FswmeiadoFY/TWsqwOpUwZ64aiG4bCGFNM0+xQSm80Tbv8Bwn94FksibVmAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2588" height="1808"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.fa0d3a1.2588.png" srcset="/assets/ideal-img/4.fa0d3a1.2588.png 2588w" width="2588" height="1808"></noscript></div></div><p>이제 css 파일이 깨지지 않고 정상적으로 렌더링되는 것을 확인할 수 있습니다.
하지만 여전히 router 기능은 동작하지 않는 것을 확인할 수 있습니다. 왜 그런 것 일까요?</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="express-server">express server<a class="hash-link" href="#express-server" title="제목으로 바로 가기">​</a></h3><p>사실 기본 옵션으로 <code>next export</code> 되었을 때의 경로가 <code>/</code> 으로 설정되어 있다는 것과 link시 이동되는 경로도 <code>/</code> 를 기준으로 하고 있는 것을 보면 결국 리소스 탐색을 위해서 <code>basePath</code> 가 필요하다는 것을 알 수 있습니다.</p><p><code>basePath</code> 지정하는 가장 쉬운 테스트 방법으로 web server에서 static file를 서빙해주는 것입니다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>도메인 자체가 리소스의 <code>/</code> 이기 때문입니다.</p></div></div><p>중요한 것은 next.js server 이외에서 돌아갈 수 있는 것입니다.
static html을 클라우드와 같은 static web host에 push하고 사용할 것이기 때문입니다.</p><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>사실 export된 static 파일을 파일시스템에서 돌려볼 일은 없을 것입니다. 그럼에도 넣은 이유는 메커니즘에 대한 설명을 위한 것이었습니다.</p></div></div><p>이를 위한 테스트를 위해서 <code>express</code> 에 express server 코드를 작성해 놓았습니다.</p><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>일부로 express 코드도 공식문서와 동일하게 맞춰놓았습니다. 코드 리딩이 쉽도록요!</p></div></div><p>아까전에 <code>index.html</code> 경로 수정해서 찝찝하므로 다시 <code>next export</code> 후 <code>express</code> 디렉터리에서 <code>npm run start</code> 를 실행해서 서빙되는 정적파일이 잘 동작하는지 확인해봅시다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA50lEQVQYlV3NzUrDQBAA4H0bsZhCq0SFFqS+gBdfwpNvUTzqoXhq9SZeCz3VGHNNuhvtG2RzWAlYL7WG2ZmRiULBgY9hhvlRB8fhRX9wmvR6J9O9oDvbabUbu61gFrQ70zA8ejkM9y/Vc5JMFnnORhs25pUzLQxrYXJ+Wy75aT5/UKuP1Yh/45uZ/T/S46qqxsra8haJGMCD954QkT7XdcN7DzJYFMWdsmU5ImZGxA0RgVjXAF81ACI2F621Y+Xc+70UiNj8lyUmYhJ/Pefco4rj+ExrfZOm6TDLsqutheSh1vo6iqLzHyVW42vEcKKdAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2588" height="1808"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.361b189.2588.png" srcset="/assets/ideal-img/5.361b189.2588.png 2588w" width="2588" height="1808"></noscript></div></div><p>next.js server 이외 다른 server에서 css 및 router가 정상적으로 동작하는 것을 볼 수 있습니다.</p><div class="admonition admonition-tip alert alert--success"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</h5></div><div class="admonition-content"><p>만약, 도메인의 하위 경로를 바꾸고 싶다면 <a href="https://nextjs.org/docs/api-reference/next.config.js/basepath" target="_blank" rel="noopener noreferrer">basepath</a> 를 참고하세요.</p></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="숨겨진-router의-원리">숨겨진 router의 원리<a class="hash-link" href="#숨겨진-router의-원리" title="제목으로 바로 가기">​</a></h3><p>맨 위에서 부터 첫 페이지 요청만 html 파일을 받고 이후에는 CSR이라고 설명했었습니다.
다시 한번 export된 파일의 구조를 한번 봐볼까요?</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAhCAYAAADgWTpIAAAACXBIWXMAAAsTAAALEwEAmpwYAAACtUlEQVQ4jW2Uy5LqNhRF+Yp0A5b8kOSHZGMwLwMNdCfc5CaZ5A66kkH+/zdWSjK4k9sZYOzyrnW8t845k1h12OaKKQ4U9ZWivpDbM/Xyhq6OPEeWqaiZRFGFPL2jr39RlAfK5g2RroiSJfO4DaKZbJhMI4vd3KjWP6OLI6Y6hRcPkr8PxOd5jXEdl99OZKYnzfckekOit4E6EmeRQxRbyq/vxNmaxAwCX34eL/5VWjjmaYd9+xNdHYjV5l7a/be0v0wjx+n6K3Z5DVRPmslBNPw/hKKmPH/DrH4MZZ9EzbOniuE3FQ0Tf6NERXZ8x3VfKNKWLGlIhSWVjkw65p7ohZUoyTe/U61u5GqNVh1JXBPHC8rYEUs3lFbCossd/fYazCT5nlhvkWaHSlukcJ5YY6RlFi846gqlNyHL1OyI0lUoPRK1qCibE6vDLyHozAv1lsyfv+oG4vRO9ORzqlHah74jNVtmyTKYih/xBGJ9ZNV/QaZd+E5PncVtcD4KPXEqG66qQKkHcff/xKI+0u5uA63oSbx7s0f5mL53fTUl2njXPZnZIbL1Z9fG9rTrV2TWhTaL1ToY/E5oidIlL+VidO1j+mTGC7N8S9f0xFkXjPgcP7sWFqk6XpY9iS/tA8/3TEfh3UwuLa07cDGOhemo8w1VtiRPGgppEWM/RjWH1xvF8kyitiRqHVprNna4nxlR80PUsK4Mrlwg1XB83nWA3Lt8EIqGvS5weoHUg9AP12NmBqGseYoa+tbgbItUvsW2iOxDOMy1qElix0VrjL3g2p/Q9yXwQWyYPM1rtLF8+5qR28u4KT4J/cM8aci7NogKN4g9NXfncELjXM/iGr1aYOwLVfNG2bwGsT/Wj2+MHIv1K8vdG1ne31dJO66Tj90zzZnv/yC9/o2zL2FHqvKAzIYcH8R/ANgt4RocoSmNAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="205" height="684"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.bcf32f0.205.png" srcset="/assets/ideal-img/6.bcf32f0.205.png 205w" width="205" height="684"></noscript></div></div><p>만약, <code>http://localhost:3000/project/facebook-react/</code> 경로에서 새로고침을 한다면 어떤 html 파일을 받아와야 할까요?</p><p>next.js server 이였다면 똑똑하게 이 요청은 <code>project/[path].tsx</code> 경로야! 하면서 SSR 이후 파일은 반환해줄 것입니다.</p><p>하지만 위의 테스트 시나리오는 next.js server 이외 처리되었습니다. 때문에 요청하는 경로에 파일이 없으면 그냥 없는 것입니다.</p><p>이를 위해서 <code>next.config.js</code> 파일을 보면 <code>trailingSlash: true</code> option 처리가 되어있는 것을 확인할 수 있습니다.</p><p>웹 개발자라면 새로고침을 하더라도 위 캡쳐사진의 파일 하이어라키 덕분에 정상적으로 router 되는 것을 확인하실 수 있습니다.</p><blockquote><p>모르겠다면 댓글주세요. 🤗</p></blockquote><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="생성되지-않은-동적경로의-경우는">생성되지 않은 동적경로의 경우는?<a class="hash-link" href="#생성되지-않은-동적경로의-경우는" title="제목으로 바로 가기">​</a></h3><p>동적인 경로의 경우 <code>getStaticPaths()</code> 으로 생성되지 않았다면 어떻게 될까요?</p><p>제가 예제 코드에서 <code>http://localhost:3000/post/[id]</code> 경로의 경우 동적 경로이지만 <code>getStaticPaths()</code> 으로 생성되지 않은 것을 확인할 수 있습니다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>export 결과물을 보면 아래와 같습니다. 이러면 당연히 새로고침하면 문제가 발생하겠죠?</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAhUlEQVQImR3OOQ7CMBQAUV8CiRC8/iwgB2EnUQJdREdBR8EBuP8VBuH+aTRK2wtWMuv2IrQTJmScTBif8O2MdlcOOqJsSEg38fx86dJGaBdCt2BlpDndC6yOZ1StI7tjZKgcbzFonwr0zVyw9CtORlRtBvZ6IPrIQxp0yDT9reD/QqmHzA/NkTr89VLWLQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="244" height="94"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.82daa4e.244.png" srcset="/assets/ideal-img/7.82daa4e.244.png 244w" width="244" height="94"></noscript></div></div></div></div><p>한번 해볼까요? express server를 시작해서 아래의 영역을 클릭해서 들어가 봅시다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA+UlEQVQYlV3NP0vDQBgG8Ps4NcUo/kuE4qDfQPBLOPkZRCmOIhSnKp2kiEvh1tYMiWea2E5ODsIRcDhIatK73JvLK9WC4gO/5eGBh2ztbh/v7R94rtsaWM112rDWaKNp0xXLpqv2xsBx3EdnZ/OEDD3vJoxjZE/PGEUTDKMXDKMYx0uT6RRHo+EdmWVZB3+iELH6Z9FhmqZdwjm/1lqjKkvI87wWQnwriqLWWgMAYJIkt4R/iE76qVBkc6lNDUpKkFKCMQaUUmo57JLXt/eemClM54AlGDTmV1Ut3hE5531yenZ+2L9/uGLhuO37/kUQBH+1GWOXlNKjL0cc4G36YJyKAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2588" height="1808"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.6a39944.2588.png" srcset="/assets/ideal-img/8.6a39944.2588.png 2588w" width="2588" height="1808"></noscript></div></div><p>정상적으로 나오는 것을 확인할 수 있습니다. <code>express/public/index.html</code> 의 파일을 받아서 CSR 처리를 통해 router 되었기 때문입니다. 이제 여기서 새로고침을 하면 어떻게 될까요?</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA2klEQVQYlV3Lv07CQBwH8HscpLEQTaw1QQZ4ABdfwsm3II46ECeEzWjCQtKpTYCB5HKtLW/Q6fobu3m96/E150Q6fJbvHxbe3z2NJ9N9GI42njeMev1B1PP86KLvR5f+1SYIbnfBzfUz+16vPw6co8gLpGkOkeUQaYY0+/lXHI/YbbefrCzL+claAFAA2g6Xoa7rBSOi97ZtoZQyTdOczrnMGAMp5ZJJKeduqLX+1VqbDuU6IlqwqqpW1lo3hHufc5nriOiLxXH8IIR445zPOOcvHTMhxGuSJI9/jS3iLE2d0VQAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2588" height="1808"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9.1d1b460.2588.png" srcset="/assets/ideal-img/9.1d1b460.2588.png 2588w" width="2588" height="1808"></noscript></div></div><p>아래와 같이 요청한 경로에 리소스가 없기 때문에 새로고침에 실패? 하는 것을 볼 수 있습니다. 만약 next.js server에서 해당 경로에서 새로고침 했다면 정상적으로 동작했을 것입니다. next.js server는 해당 경로가 어떤 리소스를 필요로 하는지 알고 있기 때문이죠</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAxUlEQVQYlV2OuwrCMBiF8zj1VkXFFESfwcWXcPItiqMO4uRlE1chk5Bq1xB78SXaZjOgYJtfUitKD3zLx+FwUMfqTqzB8IJx/1irt4hRbRKjZpJK1SQNs33E2DrjXmeKTg7deGEIvheA5wXAc3y4FoS3G7iuu0fyfl/CJ08ASEtoB1LKNYqiaKW0TdNXlmXqH+10MY7jLUqSJF9USj2UUq8S+aIQYq2Lu6JYPPjl64QQB0QpHXHOF4wxmzE2K2FzzueO44zfrwjkiwrZxWgAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2588" height="1808"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/10.f0ae2b9.2588.png" srcset="/assets/ideal-img/10.f0ae2b9.2588.png 2588w" width="2588" height="1808"></noscript></div></div><p>사실 <code>next export</code> 으로 사용한다는 것은 블로그와 같이 정말 정적인 데이터인 경우 사용하는 시나리오가 대부분 일 것이기 때문에 이와 같은 시나리오의 경우는 매우 스페셜한 케이스라고 생각됩니다.</p><p>중요한 것은 next.js server 이외 돌아가는 메커니즘을 이해하는 것입니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="사용을-추천하지-않는-exportpathmap">사용을 추천하지 않는 <code>exportPathMap</code><a class="hash-link" href="#사용을-추천하지-않는-exportpathmap" title="제목으로 바로 가기">​</a></h3><p><code>next export</code> 와 관련되어 찾아보면 <code>next.config.js</code> 의 <code>exportPathMap</code> 옵션에 대해 그냥 써놓은 글이 많습니다.</p><p>결론부터 말하면 이제 사용되지 않습니다. (사실 사용하는 법도 모르겠습니다) <code>getStaticPaths()</code> 가 동적인 경로를 반환해주기 때문이죠</p><p>해당 내용은 <a href="https://github.com/vercel/next.js/issues/10983#issuecomment-597829065" target="_blank" rel="noopener noreferrer">github issue로도 open</a> 되어 있는 상태입니다.</p><p>이걸 왜 사용하는지 제대로 설명해준 사람도 거의 없고 공식문서도 아직 내용이 반영되지 않은거 같아 시간이 많이 소모되었습니다...😾</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>에초에 동적 경로에서 <code>getStaticProps()</code> 만 사용하면 <code>Error: getStaticPaths is required for dynamic SSG pages</code> 가 출력되어 export가 안됩니다.</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>사실 제대로 next.js를 많이 사용하고 공부 헀다면 이런 질문이 나오지 않을 수 있다고 생각하기도 합니다.</p><p>하지만 그럼에도 불구하고 next.js의 SSR을 햇갈려하는 글은 간간히 보이는 걸로 보아 나만 그런건 아닌가 싶은 안심이 들긴했습니다.</p><p>특히, jsp와 같은 Server Side Script 언어와 대조를 하다보니 더욱 더 햇갈렸습니다.</p><p>보통 블로그와 같이 정적 웹을 만든다고 하면 <code>Gatsby.js</code> 가 주로 생각나고 많이 사용되는 것으로 알고 있는데 차란히 next.js로 SSG, SSR을 모두 처리하는게 더 좋지 아늘까? 하면서 더 궁금해서 SSG를 더 깊이 공부하게 된 거 같습니다.</p><p>해당 글이 next.js의 SSG 이해에 도움이 되셨으면 좋겠습니다. 언제든지 질문과 피드백은 환영합니다. 읽어주셔서 감사합니다.</p>]]></content>
        <category label="nextjs" term="nextjs"/>
        <category label="SSG" term="SSG"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Next.js에서 Recoil 사용하기]]></title>
        <id>/2021/05/06/using-recoil-in-nextjs</id>
        <link href="https://parkgang.github.io/blog/2021/05/06/using-recoil-in-nextjs"/>
        <updated>2021-05-05T18:06:21.000Z</updated>
        <summary type="html"><![CDATA[recoil 을 CSR 에서는 사용해 보았지만 next.js 와 같은 SSR 에서 사용한 사례는 찾기가 어려웠습니다. 이번 기회에 next.js 에서 recoil 을 사용하는 방법을 알아보도록 하겠습니다.]]></summary>
        <content type="html"><![CDATA[<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAABcSAAAXEgFnn9JSAAAA10lEQVQImS2N3UoCURzEZw9nv3dLz66roqgkyEIgKIJRG93UXUFEIFk9gDcSee079Cy+W+yNzF9cvfgxMDPMAMAeAJUFRp5mrdWmqzVTz2HcrDNrZgyDgFBWVZR+quVxHIqa3kjR68hdOxFv0hdtaznmOC/KZQBGAFuNiMbR9GEx8B0e3yruc3f/9zVgz3WZvC+4+F7y+eqC03GHuYk5/3igmQ+JzyLkbnMtifLEX//I9nctb0MjxW0uIxPL0+pFuq+z6vrf1iiBE3aSlmmWlY2af/JsVekBkFdNXDYdrV4AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1412" height="893"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/thumbnail.140f334.1412.png" srcset="/assets/ideal-img/thumbnail.140f334.1412.png 1412w" width="1412" height="893"></noscript></div></div><p><code>recoil</code> 을 <code>CSR</code> 에서는 사용해 보았지만 <code>next.js</code> 와 같은 <code>SSR</code> 에서 사용한 사례는 찾기가 어려웠습니다. 이번 기회에 <code>next.js</code> 에서 <code>recoil</code> 을 사용하는 방법을 알아보도록 하겠습니다.</p><p>원래는 redux를 사용했었지만 기본적으로 필요로하는 코드의 양이 많아 내가 원하는 상태를 빠르게 저장하고 빼서 쓰고 싶었는데 개발하면서 딜레이가 생기는 경험이 있었습니다.</p><p>물론 지금은 redux toolkit이 나와서 많이 해소 된거 같지만 facebook이 직접 개발하는 recoil이 앞으로 더 발전가능성이 높아서 사용하게 되었습니다.</p><p>next.js는 SSR를 지원하기 때문에 각각의 페이지 별로 정적파일이 생성되어 CSR과 햇갈릴 수 있습니다. 하지만 사용하면서 크게 다른 점은 없습니다.</p><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>기본적으로 CSR과 SSR의 차이, next.js와 recoil에 대해 기본적인 이해를 기반하고 설명합니다.</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="프로젝트를-생성해보자">프로젝트를 생성해보자<a class="hash-link" href="#프로젝트를-생성해보자" title="제목으로 바로 가기">​</a></h2><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>실습이 진행된 코드는 <a href="https://github.com/parkgang/next.js-with-recoil" target="_blank" rel="noopener noreferrer">parkgang/next.js-with-recoil</a> 에서 확인하실 수 있습니다.</p></div></div><p>먼저 아래의 명령어로 next.js 프로젝트를 실행하도록 하겠습니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">npx create-next-app</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAW0lEQVQImWNw83S1twiJvaTrHXNW19nznJa9+zlVK6dzynZe54z9Y864BIVcVzW3DWAQ1dTRFjdz7BM2tuvh1dDr4VbX62FT0uxhVNTu4dO16JY2Np/IraRhBgA16Bei+NVqaQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1554" height="392"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.7c9caaa.1554.png" srcset="/assets/ideal-img/1.7c9caaa.1554.png 1554w" width="1554" height="392"></noscript></div></div><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>공식으로 제공하는 <a href="https://github.com/vercel/next.js/tree/canary/examples/with-recoil" target="_blank" rel="noopener noreferrer">with-recoil</a> 템플릿이 있었지만 마지막 업데이트가 거즘 8개월 전이고 recoil 버전 업도 많이 된 상태이기 때문에 사용하지 않았습니다.</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="recoil를-추가하자">recoil를 추가하자<a class="hash-link" href="#recoil를-추가하자" title="제목으로 바로 가기">​</a></h2><p>아래의 명령어로 next.js 프로젝트에 recoil를 추가해줍니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i recoil</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>next.js는 pages 별로 정적 파일을 찍어낼 텐데 어떻게 하나의 상태 관리를 할 수 있을까요?</p></blockquote><p>정답은 <code>_app.js</code> 에 있습니다. next.js의 모든 pages는 렌더링을 거치기 전에 <code>/_app.js</code> 를 거칩니다.</p><p>우리가 recoil를 사용할 때 recoil 상태를 사용하는 컴포넌트의 최상위 부모 트리에 <code>&lt;RecoilRoot&gt;</code> 가 필요한데 해당 파일에 wrap 해주면 됩니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="기본적인-_appjs-구조">기본적인 <!-- -->_<!-- -->app.js 구조<a class="hash-link" href="#기본적인-_appjs-구조" title="제목으로 바로 가기">​</a></h3><p>여기서 <code>Component</code> 는 각 pages component가 들어오고, <code>pageProps</code> 는 getInitialProps를 통해 들어온 props들을 나타냅니다.</p><div class="language-jsx codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">/pages/_app.js</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-jsx codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">MyApp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter"> </span><span class="token parameter maybe-class-name">Component</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> pageProps </span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Component</span><span class="token tag" style="color:#00009f"> </span><span class="token tag spread punctuation" style="color:#393A34">{</span><span class="token tag spread operator" style="color:#393A34">...</span><span class="token tag spread" style="color:#00009f">pageProps</span><span class="token tag spread punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token maybe-class-name">MyApp</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="recoilroot-적용">RecoilRoot 적용<a class="hash-link" href="#recoilroot-적용" title="제목으로 바로 가기">​</a></h3><p>여기에 Component 코드를 <code>RecoilRoot</code> 로 wrap 해주면 됩니다.</p><div class="language-jsx codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">/pages/_app.js</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-jsx codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">RecoilRoot</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"recoil"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">MyApp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter"> </span><span class="token parameter maybe-class-name">Component</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> pageProps </span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">RecoilRoot</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Component</span><span class="token tag" style="color:#00009f"> </span><span class="token tag spread punctuation" style="color:#393A34">{</span><span class="token tag spread operator" style="color:#393A34">...</span><span class="token tag spread" style="color:#00009f">pageProps</span><span class="token tag spread punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">RecoilRoot</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token maybe-class-name">MyApp</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="기본적인-상태관리-방법은">기본적인 상태관리 방법은?<a class="hash-link" href="#기본적인-상태관리-방법은" title="제목으로 바로 가기">​</a></h2><p>자, 아직 남은 문제가 있지만 기본적인 recoil를 사용할 수 있는 상태까지 왔습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="테스트를-해보자">테스트를 해보자!<a class="hash-link" href="#테스트를-해보자" title="제목으로 바로 가기">​</a></h3><p>일단 잘 작동하는지 테스트를 해볼까요? <code>/states</code> 디렉터리를 생성하고 내부에 <code>index.js</code>를 생성해 봅시다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA40lEQVQYlW2QyU7DMBRF/RPQxFOcsSGOhzZNKE0ZKtZsKvH/n3KRHRSK1MXRfYs36B0iqx1UM4BJCyrNSir6NQMkryyadg+aeXDlwLKAXRvo7yDJC49mO4LnA5hy/7bSG8hDMWFTz+DSIOEaj7TFhnWxTlY6kObJYZieod0nen+Bdh9o+1dU7RHl9hgzQKyucZk9pvM35vcrTm9XjC9f0C4M/UGYMPEsUxYsC192SESHVNye1iBM9ZCVgSg8ZOWXLIOB8LmJBgKE5h6iPkDke8hiSSrv6EnVDqwco0OuFpf3hP8AEzOQWg1NfP8AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="224" height="230"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.79cebfa.224.png" srcset="/assets/ideal-img/2.79cebfa.224.png 224w" width="224" height="230"></noscript></div></div><p>해당 파일에 아래의 코드를 작성합니다.</p><div class="language-js codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">/states/index.js</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-js codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> atom </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"recoil"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> pageNameState </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">atom</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">key</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pageNameState"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword module" style="color:#00009f">default</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>해당 파일의 코드를 아래와 같이 변경합니다.</p><div class="language-jsx codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">/pages/index.js</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-jsx codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> useRecoilState </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"recoil"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> pageNameState </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"../states"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function maybe-class-name" style="color:#d73a49">IndexPage</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">pageName</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setPageName</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useRecoilState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pageNameState</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">Index Page!</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">pageName 상태: </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">pageName</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">          </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">            </span><span class="token tag script language-javascript function" style="color:#d73a49">setPageName</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript string" style="color:#e3116c">"IndexPage"</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript punctuation" style="color:#393A34">;</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">          </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">        </span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          현재 페이지 이름으로 상태 변경</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token maybe-class-name">IndexPage</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>router 되더라도 상태가 유지되는게 핵심이겟죠? 다른 pages도 만들어 봅시다.</p><p>아래의 파일을 생성하고 아래와 같이 코딩합니다.</p><div class="language-jsx codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">/pages/post.js</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-jsx codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Link</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"next/link"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> useRecoilState </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"recoil"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> pageNameState </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"../states"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function maybe-class-name" style="color:#d73a49">PostPage</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">pageName</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setPageName</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useRecoilState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pageNameState</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">Post Page!</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">pageName 상태: </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">pageName</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">          </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">            </span><span class="token tag script language-javascript function" style="color:#d73a49">setPageName</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript string" style="color:#e3116c">"PostPage"</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript punctuation" style="color:#393A34">;</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">          </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">        </span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          현재 페이지 이름으로 상태 변경</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Link</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">href</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">/</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">Index Pages 이동</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Link</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token maybe-class-name">PostPage</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><code>post</code> 페이지로 이동하기 쉽게 아래의 코드를 추가합니다.</p><div class="language-jsx codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">/pages/index.js</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-jsx codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Link</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"next/link"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> useRecoilState </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"recoil"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> pageNameState </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"../states"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function maybe-class-name" style="color:#d73a49">IndexPage</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">pageName</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setPageName</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useRecoilState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pageNameState</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">Index Page!</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">pageName 상태: </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">pageName</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">          </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">            </span><span class="token tag script language-javascript function" style="color:#d73a49">setPageName</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript string" style="color:#e3116c">"IndexPage"</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript punctuation" style="color:#393A34">;</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">          </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">        </span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          현재 페이지 이름으로 상태 변경</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Link</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">href</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">/post</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">Post Pages 이동</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Link</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token maybe-class-name">IndexPage</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="테스트-결과">테스트 결과<a class="hash-link" href="#테스트-결과" title="제목으로 바로 가기">​</a></h3><p>next.js를 실행해보면 index page에서 변경된 상태가 post page까지 잘 전달되는 것을 볼 수 있습니다. 그 반대도 마찬가지 입니다.<br>
<!-- -->이로써 recoil이 정상적으로 동작하는 것을 확인하였습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAABYlAAAWJQFJUiTwAAABHElEQVQYlW3Lu0rDYBjG8XfQVXDyUKG1lIbGHGqiKcYMXofQTTG5grrEzrmBgEPrkFIlKfV0GRZ1yZcQq3O9gECTfMkrhQ6C/uG3PPAAs1VbawnMGb8vGpp2oiuKqkvKkc4LTX23WjMaLH++xwnrUN6uSKzAIS8foKRoyMstrNc5rDICMg0BRVHCjc2SCu3TtnztDeY33m3eHwwz7+mRjp0hvRs9Z+P7h9zzRumFbhxD57Ijv7++ZYFP8COKCkT8bVHuuq4K3auu/DKZJFEUFbPZN50nSZ6laZ6mKaWUFnEcZ71eXwXbtg8ppcvz34qiQMdxNDBNkyWEkDAMvwgh0yAIPpemi833/dCyrCYAwAoAlACgAgDlf+wAwOoP6Sy3/57FAAsAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1224" height="974"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.2fbdc9d.1224.png" srcset="/assets/ideal-img/3.2fbdc9d.1224.png 1224w" width="1224" height="974"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAABYlAAAWJQFJUiTwAAABG0lEQVQYlW3LQUuDcBzG8d+hrkGnBgZbY2CzqXO2HVSidxHUtUjfwDrp3TcwumyHCWPpWI16Gbu05l+D1an5DgTTv/5isENQX/hcHniALdX2OgJ7zUuioWnnuqKc6XJH0XmhqR9Va0ad429OGsI+lJlKixMayMunKLVVlDoqssc8VlkB2bqAotjCgxKjwOXFlXz/MEwG7igfjt3Me57RqTOi48lLNn18yj1vkt7qhgrdu668fF1ky8UbrtdfBSL+til3XVcBy7Lk+Xz+Hb6HRRRFNEmSPEvTPE1TSikt4jjO+v2BAr1er00p3Z7/VhQFOo6jgWmaHCGEhGH4SQhZBUHwsbXabL7vh7ZtNwEAdgCAAYAKAJT/cQgAuz8Pj7gkZhyQZwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1224" height="974"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.b6049df.1224.png" srcset="/assets/ideal-img/4.b6049df.1224.png 1224w" width="1224" height="974"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="duplicate-atom-key-waring">Duplicate atom key Waring<a class="hash-link" href="#duplicate-atom-key-waring" title="제목으로 바로 가기">​</a></h2><p>동작에는 문제가 없지만 사실 next.js console에서는 Warning을 출력하고 있습니다.</p><div class="codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-text codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">Duplicate atom key "pageNameState". This is a FATAL ERROR in</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      production. But it is safe to ignore this warning if it occurred because of</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      hot module replacement.</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>이는 SSR이라서 여러면 atom을 생성하면서 경고 메시지가 출력되는 것입니다.</p><p>현재 공식 <a href="https://github.com/facebookexperimental/Recoil/issues/733" target="_blank" rel="noopener noreferrer">이슈</a>로 open 되어 있습니다. 수정까지는 시간이 꽤 걸릴 수 있으니 경고 창을 보고 싶지 않다! 라면 우회 방법을 알려드리도록 하겠습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="우회-방법">우회 방법<a class="hash-link" href="#우회-방법" title="제목으로 바로 가기">​</a></h3><p>되게 간단합니다. recoil module에서 해당 경고 메시지를 주석처리 하는 방법입니다.</p><ol><li><code>node_modules/recoil</code> 의 디렉터리에서 모든 <code>recoil.js</code> 파일의 <code>registerNode</code> 함수를 찾습니다.</li><li>해당 함수를 잘 보시면 위에서 출력되는 경고 메시지를 출력해주는 함수입니다. <code>console.warn</code> 부분을 주석하면 더 이상 출력되지 않습니다.</li></ol><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAkUlEQVQImT3KSw6CMBhF4a7GBAr9W14SaBEsD2Vi2P9ijoEYB9/g5F7lp4M+HgzrhyYsVN1G2W4UbaT2Z89oGVGN37n793WQekaqU8SUEftrLRPKNW+keGHcinELuVvIbETL85K5mdQ8UMV9x9Y7ebmSyoS252G6xiQPJPlwUUM1MthAEE+Qns70JLrnlp66vy9GQlJFrM5B5QAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="637" height="349"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.6ad0a0b.637.png" srcset="/assets/ideal-img/5.6ad0a0b.637.png 637w" width="637" height="349"></noscript></div></div><div class="admonition admonition-caution alert alert--warning"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>caution</h5></div><div class="admonition-content"><p>이 방법은 개인 에게만 적용되며 추천하지 않습니다. 실제로 key가 중복되는 경우를 구별할 수 없기 때문입니다.</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="비동기-작업은-어떻게">비동기 작업은 어떻게?<a class="hash-link" href="#비동기-작업은-어떻게" title="제목으로 바로 가기">​</a></h2><p>이번에는 <code>selector</code> 함수를 이용하여 비동기 작업을 해보도록 하겠습니다.<br>
<code>Ajax</code> 를 위해 저는 <code>axios</code> 즐겨 사용하기 때문에 해당 모듈을 설치하도록 하겠습니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i axios</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>아래의 코드를 추가합니다.</p><div class="language-js codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">/states/index.js</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-js codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> atom</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> selector </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"recoil"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">axios</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"axios"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> pageNameState </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">atom</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">key</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pageNameState"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword module" style="color:#00009f">default</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> getNameSelector </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">selector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">key</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"getNameSelector"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">get</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> res </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> axios</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"http://localhost:3000/api/hello"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> res</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="admonition admonition-caution alert alert--warning"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>caution</h5></div><div class="admonition-content"><p><code>selector</code> 에서 반환되는 값은 필요한 값만 반환해야합니다.</p><p>CSR에서는 axios의 res 값을 모두 반환해서 사용해도 문제가 없었는데 next.js에서는 모두 반환하면 에러가 발생합니다.</p><p>아마 테스트 해보시면 비동기 콜을 하는 page를 다른 페이지에서 link를 통해 들어가면 문제가 없지만 해당 page 주소로 바로 접속하면 아래와 같은 에러가 출력되면서 next.js server가 down 됩니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAg0lEQVQImT3KWw6CMBQAUbbhA7BAeVRoL7aSkBKUGN3/jsYEEz8m83OSbohI2OhlpagDWTGSl7f/UyUc0p5Ea4+TF8Y+0F2k7VdqE1H1RF6OnC/uB4MRgqx0dqFqJ8rmTlYIx2zYO+V2x4ltHCILYX4z3jem+YPzT8wQaa4zqvIo7fkCFpE8h/5nsMMAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="694" height="274"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.0e0eaa1.694.png" srcset="/assets/ideal-img/6.0e0eaa1.694.png 694w" width="694" height="274"></noscript></div></div><p>axios 에서 return 데이터 중 <code>.data</code> 만을 사용하지 않고 모두 던지면 <code>selector</code> 호출 기록은 있지만 렌더링하다가 next.js에서 run time error 가 발생합니다.</p><p>해당 문제는 <code>npm run dev</code> 일 때만 발생합니다. 같은 코드인데 build 되어 start으로 실행 된 next.js server에서는 해당 문제가 발생하지 않습니다. 하지만 dev 환경의 live server가 되지 않기 때문에 전략적으로 필요한 데이터만 return 하여 사용하는 것이 좋아보입니다.</p></div></div><p>아래의 코드를 추가합니다.</p><div class="language-jsx codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">/pages/post.js</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-jsx codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Link</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"next/link"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> useRecoilState</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> useRecoilValueLoadable </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"recoil"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> pageNameState</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> getNameSelector </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"../states"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function maybe-class-name" style="color:#d73a49">PostPage</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">pageName</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setPageName</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useRecoilState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pageNameState</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> name </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useRecoilValueLoadable</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">getNameSelector</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">switch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"hasValue"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">Post Page!</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">pageName 상태: </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">pageName</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">              </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">                </span><span class="token tag script language-javascript function" style="color:#d73a49">setPageName</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript string" style="color:#e3116c">"PostPage"</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript punctuation" style="color:#393A34">;</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">              </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">            </span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">              현재 페이지 이름으로 상태 변경</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">            </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain-text">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token known-class-name class-name">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">stringify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">contents</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Link</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">href</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">/</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">Index Pages 이동</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Link</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"loading"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">Loading...</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"hasError"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">contents</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token maybe-class-name">PostPage</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><a href="https://ko.reactjs.org/docs/code-splitting.html#reactlazy" target="_blank" rel="noopener noreferrer">SSR에서는 Suspense를 지원하지 않기 때문에</a> <code>useRecoilValueLoadable</code> 를 사용해야합니다.</p><blockquote><p>찾아보니 SSR에서도 Suspense 사용할 수 있는 방법이 있습니다! 나중에 시간이 될 때 소개하도록 하겠습니다.</p></blockquote><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="테스트-결과-1">테스트 결과<a class="hash-link" href="#테스트-결과-1" title="제목으로 바로 가기">​</a></h3><p>아래와 같이 정상적으로 렌더링 되는 것을 확인할 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAABYlAAAWJQFJUiTwAAABLUlEQVQYlW3MT0vCYBzA8d+hrkGnBANNhKW5Z+pqhzmidxHUtWh7BebJ28B30EWJhpR/UKn3MWg2ew5rPjdBsdtC27P9QujQoS98rl8QEtkdhQhXYkkyNO1MV9VTXVZUXSRF/SCTNXJ58fqoQHYhlUyX86SARD7GkqJhSamgcChiRiAo5AhKUhn3EkkVLs4v5bvOw6rVaUf37cew9zziA6vNn/ov4WA4inq9/veNblSgeluV316d8H0ywa8giBHxr01Rt9tVoV6vy67rrhljMWOMLxaL6HO5jFarNeecx0EQhM1mSwXTNE8cx8HpdIrz+RwZYzibzX5niHEco2VZGtRqtbxt25PxeOx7nuf5vv+xiVLqUUp913Vpo9EoAgBsAUASANIAkPrHPgBs/wBEfrY/Ffo5fwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1224" height="974"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.66f8fa2.1224.png" srcset="/assets/ideal-img/7.66f8fa2.1224.png 1224w" width="1224" height="974"></noscript></div></div><div class="admonition admonition-tip alert alert--success"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</h5></div><div class="admonition-content"><p>혹시라도 해당 api가 어디서 만들어 진건지? 하시면 기본 템플릿으로 만들면 생성되는 next.js api를 사용한 것입니다 😉</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="빌드-시-경고-창">빌드 시 경고 창<a class="hash-link" href="#빌드-시-경고-창" title="제목으로 바로 가기">​</a></h2><p>자 이제 문제가 없는거 같지만 아직 한가지 더 남아있습니다...<br>
<!-- -->한번 <code>npm run build</code> 으로 build를 해보시겟어요? 또 경고가 출력됩니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA6UlEQVQYlUWPSW7CQAAE5xVhM7stw3jBMONljG02m4ASkCLl/1+piDmQQ6kO3WqpRVo/yesfVPnAFBVKG/zA4PopM1ezio/IaI+QWUfV3MhMy3JV2HDm7lh4moWncKYRvZFEFPWd5vykOnxhqk/rFypvCeKKKGksItvfqI/fNOcHprqTFp1FF631Nj0TJTXCj/ZMFglzT+P6Ga5UzLyEj6Gk7wRvhM4vbNQRb52xjkuWcsfcTxiMI4aTf8Rr+nr/JTNX+67vhPRGAQMnYDAObellEW5qdN6Smo6tPhFuD0h1wlcXprJ8F/8AY159ggyhTYMAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="764" height="641"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.979f3f4.764.png" srcset="/assets/ideal-img/8.979f3f4.764.png 764w" width="764" height="641"></noscript></div></div><p>build 시점에 <code>selector</code> 에서 호출하는 api가 정상이 아니면 에러가 발생합니다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>더 정확히 말하면 try 부분의 코드를 실행하는거 같습니다. 해당 부분에 <code>console.log</code> 와 같은 코드를 삽입하면 출력되는 것을 확인할 수 있습니다.</p></div></div><p>우리는 next.js의 api를 사용하고 있기 때문에 build 시점에 next.js server가 내려가 있으니 api에 호출에 실패에서 발생하는 메시지 입니다.</p><p>아래와 같이 해당 부분을 trycatch으로 묶는다면 catch에 걸리는 것을 확인할 수 있습니다.</p><div class="language-js codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">/states/index.js</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-js codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> atom</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> selector </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"recoil"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">axios</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"axios"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> pageNameState </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">atom</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">key</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pageNameState"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword module" style="color:#00009f">default</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> getNameSelector </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">selector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">key</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"getNameSelector"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">get</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> res </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> axios</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"http://localhost:3000/api/hello"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> res</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">getNameSelector 에러 발생</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA1UlEQVQYlS3PW26DMBCFYZYRIJCAuBSMMcQJpoS0UCiVWqmX/S/mr+L2YTTzcM4njVOoJxo9UTU3hBop5JVMPJKWPfl9F4Yo1TiZXplePpnXL5SeyHNNGSuKuOZ4kLihxAslTlJdbbOoBium2QWletKkIQglblDZcdrzRJRodr4gKw1RdkZcOoJEsdtXVrPiqVuY12/MsFE1I7nocQOBF/5J9/tfnFm2H8bnD/tU8tDhHRV+3LKPGvz4hHeocUQ92oAZ3jibFd0ttOYV1W+UwzuRvNngL9ema4sLmNnQAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="659" height="475"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9.f45c8df.659.png" srcset="/assets/ideal-img/9.f45c8df.659.png 659w" width="659" height="475"></noscript></div></div><p>결론적으로 경고 메시지가 출력되어도, catch에 걸리더라도 <strong>구어진 정적파일은 문제가 없으며 next.js server가 실행했을 때 정상적으로 동작합니다.</strong></p><p><code>selector</code> get부분에 api EndPoint를 next.js 내부 api 말고 build 시점에 살아있는 api으로 호출한다면 경고 메시지 없이 빌드 되는 것을 확인할 수 있습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>사용하는데 크게 어려운 점은 없지만 비동기 콜 부분에서 많은 시간을 소비하였습니다.</p><p>에러 발생 시 정보가 생각보다 디테일 하지 않았고 CSR에서는 되던건게 안되서 코드를 잘못짠건가?🧐 했습니다.</p><p>특히 axios 결과 값을 모두 return 했을 때 next.js server가 down되는 부분에서 많은 시간을 소비하였습니다.</p><p>어서 recoil이 공식 릴리즈되서 많은 사람들이 사용했으면 좋겠네요 언제든지 질문과 피드백은 환영합니다. 읽어주셔서 감사합니다.</p>]]></content>
        <category label="nextjs" term="nextjs"/>
        <category label="recoil" term="recoil"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[parkgang.log(2020)]]></title>
        <id>/2021/05/06/2020-retrospective</id>
        <link href="https://parkgang.github.io/blog/2021/05/06/2020-retrospective"/>
        <updated>2021-05-05T18:05:10.000Z</updated>
        <summary type="html"><![CDATA[공부한 걸 잘 정리하는 습관은 있었는데 한 해를 회고한 적이 없었습니다. 2020년을 회고하기엔 조금 늦은 2021 2분기 이지만 짧게 첫 회고를 해보려고 합니다.]]></summary>
        <content type="html"><![CDATA[<p>공부한 걸 잘 정리하는 습관은 있었는데 한 해를 회고한 적이 없었습니다. 2020년을 회고하기엔 조금 늦은 2021 2분기 이지만 짧게 <code>첫</code> 회고를 해보려고 합니다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>깃허브 첫 가입!</h5></div><div class="admonition-content"><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAABYlAAAWJQFJUiTwAAAASUlEQVQImRXKMQ6AIAxAUY7QFi1GoGI0Dq7e/2zfMLztpWULfB/k0tG1Il7RqXZkBPkaWATJvGHeySWwmUtD14ZGIO+JfjfyHPz+zhnPwQVB+wAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1532" height="406"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.86b1e98.1532.png" srcset="/assets/ideal-img/1.86b1e98.1532.png 1532w" width="1532" height="406"></noscript></div></div></div></div><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>웹 제품을 만들기 위한 공부의 서막</h5></div><div class="admonition-content"><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAlklEQVQImV3MPQ7CMAxA4ZyHtolTx07cH8RBWZBYuAUjFbCUMsFFqg5GgU4Mn972jHTbg/S7t0UePcvkQpws8opGiO0Lmu3JAKVLzaIuROW21yCdehaltT426khGU9V0tshqkWeLvLgQv4DS4llmTJ1CSIOxSEO+/bMZZqxVTQ+zcX5fAD4LwGsBeM9KH34AbwXgVAIeP/OGSmbg2q3iAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="4436" height="2246"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.e76f009.4436.png" srcset="/assets/ideal-img/2.e76f009.4436.png 4436w" width="4436" height="2246"></noscript></div></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="tldr">TL;DR<a class="hash-link" href="#tldr" title="제목으로 바로 가기">​</a></h2><ol><li>첫번째로 작성해보는 회고!</li><li>첫 직장! 🔥</li><li>전체적으로 웹 제품을 만드는 방법에 대해 알게 되었다.</li></ol><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="나의-2020년은">나의 2020년은?<a class="hash-link" href="#나의-2020년은" title="제목으로 바로 가기">​</a></h2><p>2020년은 처음으로 깃허브에 가입한 해이기도 하고 SW 엔지니어라는 직업으로 첫 출근을 한 해이기도 합니다.</p><p>저의 첫 개발은 C언어로 펌웨어 프로그래밍을 하다가 여기까지 오게 되었는데 그래서 그런지 front-end? back-end? 로 왜 구분을 하는 것이고 sql을 왜 배워야 하지? 이런 생각이 난잡하던 해이었습니다.</p><blockquote><p>전 C언어만 알면 모든 것을 해결할 수 있는 줄 알았거든요... 진심으로... 🥴</p></blockquote><p>그래도 다행히 2분기부터 대학 친구와 함께 JSP 스터디를 진행하면서 정신을 차리게 되었습니다. 아마 이 스터디가 없었더라면 전 매우 많이 후퇴되어 있었을 거 같아요.</p><p>근데 스터디를 진행하면서 느낀 것은 back-end의 경우 java를 사용헀기 때문에 C-family programming languages 답게 문법적으로나 동작에 대해서 이해가 안 되는 부분이 적었습니다.</p><p>문제는 front-end 이었는데요 태어나서 처음 보는 마크업 언어를 보고 이게 프로그래밍 언어인지... 아니라고 하기엔 문법이라는 게 보이긴 하는데 그래서 마크업이라고 하는 건 가... 이걸 어떻게 사용해야 하는지 감조차 안 오는 상태이었습니다.</p><blockquote><p>JS 친구는 덤이었습니다. C언어를 하다가 JS를 보니까 외계어인 줄 알았어요</p></blockquote><p>그래서 스터디가 종료되고 HTML, CSS, JS와 같이 front-end에 부족한 부분을 알아서 공부를 하면서 자연스럽게 프로그램 배포는 어떻게 되는 거지? 서버에 배포하면 된다고? 서버는 어떻게 만드는데? 원리는? 이런 식으로 계속 파고들어서 공부하게 되었습니다.</p><p>마침 회사에서도 클라우드와 DevOps를 경험하고 있던 시점이어서 서버와 관련된 모르는 것을 동료에게 물어보며 성장할 수 있었습니다.</p><blockquote><p>모두 고마워요! 🥰</p></blockquote><p>그 결과 연말에는 <a href="https://github.com/parkgang/modern-tier" target="_blank" rel="noopener noreferrer">modern-tier</a> 프로젝트를 진행하면서 CI/CD 자동화 라인까지 직접 만들어보게 되었습니다.</p><p>돌이켜보면 짧은 시간 안에 많은 성장을 했다는 것이 느껴집니다.</p><blockquote><p>사실 그냥 제 자신을 갈아 넣었습니다.</p></blockquote><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>전반적으로 웹 애플리케이션 제품 개발 한 사이클을 돌려서 기본을 배울 수 있는 시간이었습니다.</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="앞으로는">앞으로는?<a class="hash-link" href="#앞으로는" title="제목으로 바로 가기">​</a></h2><p>아직 부족한 것이 많지만 <code>2020</code> 년에는 웹 애플리케이션을 만드는 아주 큰 틀을 이해하는 시간이 되었습니다.</p><p>개발하면서 어떤 점이 부족하고 어떤 것을 더 중점으로 공부해야 하는지 index가 잡히는 계기가 되었습니다.</p><p><code>2021</code> 년에는 전반적으로 배운 기술을 복습하고 고도화할 수 있도록 노력하는 한 해가 되기를 목표하여 글을 마무리하겠습니다.</p>]]></content>
        <category label="개인 회고" term="개인 회고"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[GitHub Blog 시작 이유]]></title>
        <id>/2021/05/06/why-start-github-blog</id>
        <link href="https://parkgang.github.io/blog/2021/05/06/why-start-github-blog"/>
        <updated>2021-05-05T17:05:10.000Z</updated>
        <summary type="html"><![CDATA[왜 굳이 SaaS 블로그를 사용하지 않고 깃허브 블로그를 선택하게 되었을까? 깃허브 블로그 첫 포스트로써 왜 깃허브 블로그를 사용하게 되었는지 알아보도록 하겠습니다.]]></summary>
        <content type="html"><![CDATA[<p>왜 굳이 <code>SaaS</code> 블로그를 사용하지 않고 깃허브 블로그를 선택하게 되었을까? 깃허브 블로그 첫 포스트로써 왜 깃허브 블로그를 사용하게 되었는지 알아보도록 하겠습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="이전의-나는">이전의 나는?<a class="hash-link" href="#이전의-나는" title="제목으로 바로 가기">​</a></h2><p>나의 첫 개발 블로그는 <code>velog</code> 에서 시작되었습니다.</p><p>처음에는 열정적으로 잘 작성하였지만 어째 사람들에게 정보를 공유하기 위한 개발 블로그 라는 성격 보다는 내가 공부한 내용을 복습하고 나중에 보려고 작성하는 느낌이 강하게 들었습니다.</p><p>더구나 이미 있는 글을 나에게 맞게 바꾸어 작성하게 되는 나를 발견하고 이럴꺼면 개인 노트에 작성하는게 더 효율적이라는 생각을 하게되었습니다.</p><p>이때부터 이전부터 봐왔던 notion을 공부해서 알게 된 지식을 모두 정리하기 시작하였습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAARCAYAAADkIz3lAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB/UlEQVQokU2STW7bMBCFdZI2v5ITSyIl/gwlUSRlObZTOE2KLIoC8aJID9QarYGiQE8QIAfIxRLJ9hS0g7aLtyE+vveGnCDN2efZrHxSunyIc/mYsr0SJh+pVA+yMk+Ew5cgjOnPqw/XqGqD5xnHhMm/ShlgygHjXPwOBin9Wrkxmnb6LLVdQ+3WoN1a1c2aKf1MhMKUye9BFJNlPRrj7cdP3fXNLc7mNzi+nOPF5RzHl1c9L2uMM74KBgldlrbF2fy6c+0EobIotUVZGVS165mqfPQqOCf5UpkGQduOymLXaS+JmVA9l8U/0PfIlO6gbhBMgz7OnyVM9oQDJuwVTPxkTHRxLjAVCkVlfQISDv+BNF9SDkg5dFQAZhKQcLnvaFxPvbOPjtJs6dwYa2262pYoixJLZzGDAu1k2lNRvIL+edoJytp2YCxSWeL0/Q3y0qBt3/VEvjr6aN8xYbIjoNE7uGaCGZR+2n7/SzswW0JtkMiiE26KQls0boSEK9Sm6f2Fc5Kvgiih38zFFEGbrjLNNodiW1mzTYXaSm26XFV78Izk35rZHKumfSlcu8mg3AwzsUk5bOJcvCQMcEjZjyCM01U9anflq9EURalxuNsi4Tv6hfD6Fbw5OmGHYXT/9ji8G6bZ4jg6WxychIvD02hxcBrdHYWD++NwIP4AlQ3+hebVHQsAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="4014"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.ab6503e.2384.png" srcset="/assets/ideal-img/1.ab6503e.2384.png 2384w" width="2384" height="4014"></noscript></div></div><blockquote><p>쭉쭉 늘어나는 페이지들...</p></blockquote><p>사실 notion에 웹 공유 기능이 있어서 열심히 notion에 작성하고 웹에 공유해서 블로그 처럼 사용하려고 했습니다. 하지만 터닝 포인트🔥 가 발생하게 되는데</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="왜-깃허브-블로그">왜 깃허브 블로그?<a class="hash-link" href="#왜-깃허브-블로그" title="제목으로 바로 가기">​</a></h2><p>제품(SW)에 대해 아주 간단하고 사소한 오류라도 사용자 풀이 적다면 해결 방법에 대해 찾기가 어렵습니다.</p><p>물론 개발자 실력이 좋다면 그동안 쌓아온 지식을 기반하여 아주 수월하게 트러블 슈팅을 진행할 수 있을 겁니다.</p><p>하지만 깊은 지식 없이 SW가 <code>돌아가는 것에 치중</code> 하여 학습된 상태로 예상 하지 못한 새로운 이슈를 만난다면 당황스럽고 사전에 같은 문제를 고민한 글에 의존하게 됩니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA9klEQVQYlU2Oa0rDQBCA9y7ShGyyu9ndzCYxNoZKH9ZIRAX/C96nSDCIIPUAQg6g52ppSlYmRPHHx7w+ZoZo0I86Sb9Pi9lnnOUtU9BybVquoJWQYO9LQvJEqFCvUhurhLQ0jCxXYLk2f4goxt47ipswiu3lutwvy+posvwoTTogong/iBpqFJ9DSGyxWB2ms7lNpsWAyXIrTdqN4guhXA7iqqwO5xeLXpq0Zwp6rg3yTxRyg0V5c7e7qm47SM+6UcC4G3+siS/UG26cr68tnsYch79gzTVsyYnr3Ts0+HB9XjuUNRPPbyZe0Dg0wLx2fbb1mHj4ATYxc/9Ph8yGAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2384" height="1928"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.416b01c.2384.png" srcset="/assets/ideal-img/2.416b01c.2384.png 2384w" width="2384" height="1928"></noscript></div></div><p>저는 각 토픽에 맞게 트러블 슈팅 내용을 notion에 기록하고 있었습니다.</p><p>요근래 공부하고 있는 부분에서 트러블 슈팅을 진행하면서 나와 같은 문제를 고민한 글이 많지 않다는 것을 직접 체감하게 되었습니다.</p><blockquote><p>이 트러블 슈팅 기록을 또 notion 작성해?</p></blockquote><p>물론 notion에도 계속 작성되고 있습니다. 하지만, 이전에 트러블 슈팅 경험을 살펴보면 글의 퀄리티 유무와 관계 없이 누군가 나와 같은 문제를 겪고 대충이라고 해결 방법을 적어 놓은 글을 보았을 때 많은 도움이 된다는 것을 느끼게 되었습니다.</p><blockquote><p>물론 공식문서를 보고 차근차근 배우고 트러블 슈팅하는게 맞지만 😔</p></blockquote><p>딱, 이맘때쯤 깃허브 블로그로 기술 분석에 대해 제가 궁금한 점을 딱 잡아서 작성된 글이 있었는데 그걸 읽고 반해버렸습니다.</p><p>notion에 작성할 때 아래의 Side effects를 고민하고 있던 시절이여서 깃허브 블로그가 더 매력적으로 다가왔습니다.</p><ol><li>이미지와 같은 정적파일이 날라가면 어떡하지?</li><li>다른 서비스로 마이그레이션은?</li><li>웹 공유를 하더라도 사람들이 많이 찾아줄까?</li></ol><p>그래서 전략적으로 듣고 까먹지 싫은 건 notion에 작성하고 사람들에게 가치를 전달할 수 있는 글은 깃허브 블로그에 작성해보자는 마음으로 시작되었습니다.</p>]]></content>
        <category label="GitHub" term="GitHub"/>
        <category label="blog" term="blog"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[왜 React.FC는 안 쓰일까?]]></title>
        <id>/2021/01/01/why-is-react-fc-not-used</id>
        <link href="https://parkgang.github.io/blog/2021/01/01/why-is-react-fc-not-used"/>
        <updated>2021-01-01T03:00:00.000Z</updated>
        <summary type="html"><![CDATA[typescript 로 React Function Component Type을 지정하다 보면 만나게 되는 React.FC 이 친구는 왜 안 쓰일까?]]></summary>
        <content type="html"><![CDATA[<p><code>typescript</code> 로 <code>React</code> Function Component Type을 지정하다 보면 만나게 되는 <code>React.FC</code> 이 친구는 왜 안 쓰일까?</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="props">props<a class="hash-link" href="#props" title="제목으로 바로 가기">​</a></h2><p><code>React.FC</code> or <code>직접 type</code> 를 정의하는 방법이 있습니다.</p><p>원래에는 <code>React.FC</code> 가 <code>defaultProps</code> 혹은 <code>props.children</code> 이슈 때문에 직접 type를 정의하는 방법이 좋다고 생각했지만 생각을 해보니 <code>React.FC</code> 를 이용해서 충분한 우회 방법을 찾아냈습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="공식적인-가이드">공식적인 가이드<a class="hash-link" href="#공식적인-가이드" title="제목으로 바로 가기">​</a></h2><p>js으로 개발된 react를 ts으로 마이그레이션을 위해 개발자가 연구를 통해 탄생한 type이 <code>React.FC</code> 이라고 생각됩니다.</p><p>이후 발생하는 이슈도 언젠간 공식적으로 수정될 확률이 높고 안전하게 사용할 수 있습니다.</p><blockquote><p>반대로 말하면 어떻게 미래에 어떤 방식으로 바뀔지 보장할 수 없는 위험이 있습니다.</p></blockquote><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="짧아지는-코드">짧아지는 코드<a class="hash-link" href="#짧아지는-코드" title="제목으로 바로 가기">​</a></h2><p>type를 정의하고 사용하면 아마 아래와 같은 코드가 나오게 됩니다.</p><div class="language-ts codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-ts codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">interface</span><span class="token plain"> </span><span class="token class-name">MyProps</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  mark</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  optional</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">onClick</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">void</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Greetings</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> mark</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> optional</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> onClick </span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> MyProps</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">JSX</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Element </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><code>MyProps</code> 으로 props type를 정의하고 함수의 return type까지 <code>JSX.Element</code> 으로 정의하였습니다.</p><p>이번엔 <code>React.FC</code> 으로 작성해보도록 하겠습니다.</p><div class="language-ts codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-ts codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">interface</span><span class="token plain"> </span><span class="token class-name">MyProps</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  mark</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  optional</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">onClick</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">void</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> Greetings</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">FC</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">MyProps</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> mark</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> optional</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> onClick </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>사실 <code>React.FC</code> 이나 크게 차이는 없지만 <code>React.FC</code> 는 props와 return까지 한번에 검사를 해주기 때문에 짧은 코드를 생산할 수 있습니다.</p><blockquote><p>Lint을 빡세게 잡아서 모든 함수의 return까지 명시를 해야하는 rule이 있다면 <code>React.FC</code>은 좋은 선택일지도 모릅니다.</p></blockquote><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="defaultprops">defaultProps<a class="hash-link" href="#defaultprops" title="제목으로 바로 가기">​</a></h2><p><code>React.FC</code> 를 사용하면 <code>.defaultProps</code> 을 작성해도 체크되지 않는 버그가 있습니다.</p><p>ES6 문법인 기본값 매개변수를 이용하여 해결할 수 있습니다.</p><div class="language-ts codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-ts codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> Greetings</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">FC</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">MyProps</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  mark</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  optional </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Hi"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  onClick</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>props이름이 변경되면 파라미터의 props이름과 defaultProps이름을 2가지 모두 변경해야 하는데 한번에 함수 파라미터에서 변경하는게 더 편해보입니다.</p><p>이미 진행된 react.js에서 defaultProps를 사용했다면 react.ts으로 마이그레이션을 위해서는 다시 파라미터에 기본값을 명시해야되기 때문에 오히려 불편한 방법이 될 수 있을꺼 같습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="propschildren">props.children<a class="hash-link" href="#propschildren" title="제목으로 바로 가기">​</a></h2><p>children 값이 옵셔널 형태라 사용유무를 모르겠다면 사용되는 component의 type에 명시적으로 선언해주면 됩니다.</p><div class="language-ts codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-ts codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">interface</span><span class="token plain"> </span><span class="token class-name">MyProps</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  children</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ReactNode</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="js스러워서">js스러워서?<a class="hash-link" href="#js스러워서" title="제목으로 바로 가기">​</a></h2><p>일부의 <a href="https://fettblog.eu/typescript-react-why-i-dont-use-react-fc/" target="_blank" rel="noopener noreferrer">TypeScript + React: Why I don't use React.FC</a> 중에서 props를 일일이 지정해주는것이 간단하고 JavaScript에 훨씬 더 가깝다고 말합니다.</p><p>명시가 직관적이고 간단하며 추후, <code>React.FC</code> 가 변경된다고 해도 props를 받아오는 type를 명시한 것은 ts의 기능이기 때문에 하위호환성도 크게 걱정되지는 않을겁니다.</p><p>하지만 js에 가깝도록 ts에서 코딩하는 이유는 모르겠습니다. 강력한 타입의 기능을 사용하기 위해 ts를 사용해야하는데 class 기반의 React의 유물인 defaultProps를 사용해서 type을 명시한다는거 부터가 ts에서 사용하는 이유는 사라지고 있는거 같습니다.</p>]]></content>
        <category label="react" term="react"/>
        <category label="typescript" term="typescript"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Blue-Green 배포 방법을 이용하여 무중단 배포를 구현해보기 (with docker)]]></title>
        <id>/2020/11/29/implement-blue-green-deployment-with-docker</id>
        <link href="https://parkgang.github.io/blog/2020/11/29/implement-blue-green-deployment-with-docker"/>
        <updated>2020-11-29T04:00:00.000Z</updated>
        <summary type="html"><![CDATA[Blue-Green 배포 방법을 이용하여 무중단 배포를 구현해 보겠습니다.]]></summary>
        <content type="html"><![CDATA[<p>Blue-Green 배포 방법을 이용하여 무중단 배포를 구현해 보겠습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="메커니즘">메커니즘<a class="hash-link" href="#메커니즘" title="제목으로 바로 가기">​</a></h2><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA0ElEQVQYlT2PTU/CQBRF5///E92wkJULViZKkBgDBsWkptR+gIF2Wjvt0HlzzFTxbt57ybm59yndtOi6wXtPUJiXnb87SN1FR6ZPMXhhcA4nHmsMzdsG87n7h9UyKZm9pCPonCDiR0M2uea0ePgFRVDv+5pldBjB4HQifOUpfV3hrcU5RwhXj7uS21UygvYcoh3TVcJ8mzCcOxgsUpWoQndEe43pDVlecKoarhYf3G9i2vabyXxLmh1Ql++63qK1RmRg9pqyjovQjpvnhLxs+QExAAni13Iw/QAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1016" height="728"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.b0ae04e.1016.png" srcset="/assets/ideal-img/1.b0ae04e.1016.png 1016w" width="1016" height="728"></noscript></div></div><p>로드 밸런서가 <code>blue 컨테이너</code> 와 <code>green 컨테이너</code> 를 로드 밸런싱 합니다.</p><p>배포되는 순간 <code>blue 컨테이너</code> 가 활성화 되어있으면 배포해야하는 새로운 파일을 <code>green 컨테이너</code> 로 실행시킵니다. 이 순간에만 blue와 green컨테이너가 up됩니다. 또한, 두개의 컨테이너가 로드 밸런싱이 진행됩니다.</p><p>일정 시간이 지난뒤 <code>blue 컨테이너</code> 을 down시킵니다. down 하더라도 이전에 배포된 <code>green 컨테이너</code> 가 up 되어있기 때문에 연결이 중단되지 않습니다. 반대로 <code>green 컨테이너</code> 가 up되어있으면 위의 작업이 반대로 진행됩니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="소스코드-작성">소스코드 작성<a class="hash-link" href="#소스코드-작성" title="제목으로 바로 가기">​</a></h2><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>실습이 진행된 코드는 <a href="https://github.com/parkgang/helloworld-docker-blue-green" target="_blank" rel="noopener noreferrer">parkgang/helloworld-docker-blue-green</a> 에서 확인하실 수 있습니다.</p></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="deploysh">deploy.sh<a class="hash-link" href="#deploysh" title="제목으로 바로 가기">​</a></h3><p><a href="https://velog.io/@jeff0720/Travis-CI-AWS-CodeDeploy-Docker-%EB%A1%9C-%EB%B0%B0%ED%8F%AC-%EC%9E%90%EB%8F%99%ED%99%94-%EB%B0%8F-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-2#node-koa-server-%EC%86%8C%EC%8A%A4%EC%BD%94%EB%93%9C-%ED%81%B4%EB%A1%A0" target="_blank" rel="noopener noreferrer">Travis CI, AWS CodeDeploy, Docker 로 배포 자동화 및 무중단 배포 환경 구축하기 - (2)</a> 글을 참고하여 작성되었습니다.</p><p><strong>다만, 다른 점은 docker image를 build후 사용하기 때문에 up할 때 <code>--build</code> 가 추가되어 있습니다.</strong></p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA2UlEQVQImV3I0UrDMAAF0PyR0jnd3EQUv8YH/0WLD/0SEUR8mZvEWmVN02T7AXGpJmtd06ReWX3zwuFyLzk6Ob0eHZ9lQX9M9wajuNc/jIPOMN7tHdCdYJ8NhuOITGb0gSYMr2mONBOYs79mXGLOeLfp88sjef9QtxvrAKAB0P6z/VB86nuiVupGf2kYbeqqLJ1rGmeq0um1ceV3VW+cxapQd6RQxbSuLbxvO9tY72G9Q+M93E8LvTYxmc6eztOMR0IuL3O5CLmQoRCLUMhlyHN5lXERJcnbxS+3T8I+EX8U0wAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3808" height="2242"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.418a303.3808.png" srcset="/assets/ideal-img/2.418a303.3808.png 3808w" width="3808" height="2242"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="docker-composeyml-시리즈">docker-compose.yml 시리즈<a class="hash-link" href="#docker-composeyml-시리즈" title="제목으로 바로 가기">​</a></h3><p>위의 글을 참고하여 작성되었습니다.</p><p>version을 3.3으로 작성해야합니다. docker version 맞게 3.8으로 작성시 오류가 발생합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA6ElEQVQYlU1OTU+DQBDd/1qwtIqJ+he8NvHqVS/Gk3ox/oiGI9FmjxR2dmdZCCFbmGUM2IOTTDIvb96HiJKL3fXNHVyltz9xksrVeiujOJFRfClX8eaw2aZVFK0fRXEsXqZpYiLiruvYe7/c/6dpmk9hjHmawTAMg7U2eO8DEQVmDiGEYeYQ8V1Ya5/PjyMict/3PI4jzynMvFgDwIdAxMXRe3/SWpMxhrQxhLUjW7vTXKcoijfhnHtdpETsnPvbpmXXdlw37dK5qqovIaW8R8QDAOzLssyUUpkCyJTWmQK9B4DvPM8ffgH2P/UwaRpXQwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.d49fbbd.2956.png" srcset="/assets/ideal-img/3.d49fbbd.2956.png 2956w" width="2956" height="2222"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="shell-script">shell script<a class="hash-link" href="#shell-script" title="제목으로 바로 가기">​</a></h3><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">chmod</span><span class="token plain"> +x ./deploy.sh</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">./deploy.sh</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="로드밸런서-구축">로드밸런서 구축<a class="hash-link" href="#로드밸런서-구축" title="제목으로 바로 가기">​</a></h2><p>위의 아키텍처 처럼 blue에서 green으로 스위칭 되는 순간 로드밸런싱이 필요합니다.</p><p>이전에 포스트 되었던 <a href="/blog/2020/11/29/implement-load-balancer-using-nginx-with-docker/">nginx를 이용해 Load Balancer 구현 해보기 (with docker)</a> 를 응용해서 로드 밸런서 구축하도록 합니다.</p><div class="language-nginx codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">/nginx/nginx.conf</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-nginx codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token directive keyword" style="color:#00009f">user</span><span class="token directive">  nginx</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">worker_processes</span><span class="token directive">  </span><span class="token directive number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">error_log</span><span class="token directive">  /var/log/nginx/error.log warn</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">pid</span><span class="token directive">        /var/run/nginx.pid</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">events</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">worker_connections</span><span class="token directive">  </span><span class="token directive number" style="color:#36acaa">1024</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">http</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">upstream</span><span class="token directive"> tomcat-project</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># least_conn;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># ip_hash;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># server 52.231.50.84:10001 weight=1 max_fails=1 fail_timeout=1s;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># server 52.231.50.84:10002 weight=1 max_fails=1 fail_timeout=1s;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token directive"> 52.231.50.84:10001</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token directive"> 52.231.50.84:10002</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">listen</span><span class="token directive"> </span><span class="token directive number" style="color:#36acaa">80</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">server_name</span><span class="token directive"> 52.231.50.84</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">location</span><span class="token directive"> /</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token directive keyword" style="color:#00009f">proxy_pass</span><span class="token directive"> http://tomcat-project</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># port 스위칭 후 사용되지 않는 포트를 구분하기 위해 timeout을 1초로 설정</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token directive keyword" style="color:#00009f">proxy_connect_timeout</span><span class="token directive"> </span><span class="token directive number" style="color:#36acaa">1s</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token directive keyword" style="color:#00009f">proxy_send_timeout</span><span class="token directive"> </span><span class="token directive number" style="color:#36acaa">1s</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token directive keyword" style="color:#00009f">proxy_read_timeout</span><span class="token directive"> </span><span class="token directive number" style="color:#36acaa">1s</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token directive keyword" style="color:#00009f">send_timeout</span><span class="token directive"> </span><span class="token directive number" style="color:#36acaa">1s</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>해당 부분은 nginx에 자세히 알아야합니다. 아래의 내용을 참고하세요.</p><ol><li><a href="http://jaynewho.com/post/18" target="_blank" rel="noopener noreferrer">nginx 의 구조 친절한 설명</a></li><li><a href="https://architectophile.tistory.com/12" target="_blank" rel="noopener noreferrer">추후 product에서 될만한 자세한 설명</a></li><li><a href="https://brunch.co.kr/@alden/58" target="_blank" rel="noopener noreferrer">max_fail과 fail_timeout 옵션 설명</a></li><li><a href="https://www.lesstif.com/system-admin/nginx-load-balancing-35357063.html" target="_blank" rel="noopener noreferrer">로드 밸런싱 메소드</a></li><li><a href="https://velog.io/@damiano1027/Nginx-Nginx%EC%99%80-SpringBoot-%EB%82%B4%EC%9E%A5-Tomcat-%EC%97%B0%EB%8F%99" target="_blank" rel="noopener noreferrer">nginx conf 작성방법 잘 나와있는 듯</a></li></ol></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="docker-in-docker-volume-mount-문제">docker in docker volume mount 문제<a class="hash-link" href="#docker-in-docker-volume-mount-문제" title="제목으로 바로 가기">​</a></h3><p>로드 밸런서의 기법을 바꾸고 싶으면 어떻게 해야 할까요? 가장 편리한 방법은 <code>config</code> 를 따로 관리하는 것입니다. <code>config</code> 가 변경될 때만 로드 밸런서가 알아서 restart되면 좋겟지만 추가적인 shell 작업이 필요할 겁니다.</p><p>때문에 현재 source code는 <code>config</code> 만 따로 관리하는 전략인데 문제는 jenkins가 DinD 형태라 볼륨 마운트가 host로 바인딩되어 git내부로 작성된 <code>config</code> 를 연결할 수가 없습니다.</p><p>해결방법은 DinD를 사용하지 않거나 심볼릭 링크를 사용 혹은 image으로 해당 <code>config</code> 를 추가하는 방법이 있습니다. 현재는 비효율적이지만 문서화가 가능하도록 docker-compose를 작성하고 로드 밸런서를 rebuild 할 때마다 image를 생성하도록 되어있습니다.</p><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>image의 ADD는 현재 컨테이너로 정상적으로 추가가 가능합니다.</p></div></div><div class="admonition admonition-tip alert alert--success"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>다른 우회방법으로 docker-compose를 사용하지 않고 command으로 하는 방법이 존재합니다.</h5></div><div class="admonition-content"><ol><li><strong><code>-v</code> 를 docker-compose으로 동일하게 작성하더라도 동작되지 않습니다.</strong></li><li>볼륨 마운트를 jenkins가 host와 볼륨 마운트된 경로에 추가적으로 붙인 형태입니다. <code>\</code> 가 아닌 <code>/</code> 으로 명시해야합니다.</li></ol><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> run -d --name test123 -p </span><span class="token number" style="color:#36acaa">80</span><span class="token plain">:80 -v C:/Resources/docker/jenkins/jenkins_home/workspace/helloworld-docker-blue-green/nginx/nginx.conf:/etc/nginx/nginx.conf nginx</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="사용하지-않는-port-스위칭-문제">사용하지 않는 port 스위칭 문제<a class="hash-link" href="#사용하지-않는-port-스위칭-문제" title="제목으로 바로 가기">​</a></h3><p><code>10001</code> (blue)와 <code>10002</code> (green)포트를 사용합니다.</p><p>blue를 컨테이너가 up되면 green 컨테이너는 down됩니다. 하지만 down 되었다고해서 <code>10002</code> port가 사용이 불가능 하지는 않습니다. 다만 해당 포트로 포워딩 해야 할 자원은 없기 때문에 오랜시간 동안 response를 기다릴 것 입니다.</p><p>로드 밸런서에 <code>10001</code> 과 <code>10002</code> 를 로드 밸런싱 하도록 되어있기 때문에 blue-green 배포 시 필연적으로 하나의 port는 제대로된 역할을 못하여 새로고침시 오랜시간동안 기달려야 합니다.</p><blockquote><p>로드 밸런서는 하나의 포트가 사용되지 않더라도 접속은 되기 때문에 요청이 들어올 때 까지 기다리는 것입니다.</p></blockquote><p>정상적인 방법은 아직 찾지 못하였으나 사용하지 않는 포트에 접속할 수 없는 상태코드를 뿌려주는 것이 맞아보입니다.</p><p><strong>현재는 <code>conf</code> 에서 timeout이 1초 이상 넘어가면 접속하지 않도록 되어있습니다.</strong></p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="실습에-사용된-코드-설명">실습에 사용된 코드 설명<a class="hash-link" href="#실습에-사용된-코드-설명" title="제목으로 바로 가기">​</a></h2><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAYAAAALpr0TAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA2klEQVQYlV2OO07EMBgGc/8aCdHQwCmAE9BQIyqiTbReJ34/snGUQXYQCCxNN9/87owxDMPAOI5cpMRa20gp8/TyzO3dDQ+P93QxRuZ5pg6qkHNmLYWYEmJShLRQX+eca1IVUkqNnBJmmuhPI0LOlG07ikIInPXknCilsJWClpLzWaCMxflAV0ut6jzaHrgQMdailGpn933//qNSaGPR1qHNMVJK/xODxxnNuq6s12ujlJUQAtM0/Yqni+LtoyemwLIsjTry3v8VP8XM63tPiP4Qa7WJASnlj/gFWMpXXy/iATIAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1830" height="1696"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.3398e60.1830.png" srcset="/assets/ideal-img/4.3398e60.1830.png 1830w" width="1830" height="1696"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="로드-밸런서-활성화">로드 밸런서 활성화<a class="hash-link" href="#로드-밸런서-활성화" title="제목으로 바로 가기">​</a></h3><p>먼저 로드 밸런서를 활성화 해야합니다.</p><p>코드를 살펴보면 실제로 tomcat 컨테이너가 올라가는 port는 <code>10001</code> (blue)과 <code>10002</code> (green)입니다.</p><p>blue-green 배포가 진행되면 <code>10001</code> 과 <code>10002</code> 이 계속 스위칭 됩니다. 어느 포트로 스위칭 되어도 같은 접속 포트로 접속한 이펙트를 위해서 로드 밸런서가 활성화되어야 합니다. 때문에 활성화 되어있지 않으면 <code>80</code> (http)으로 접속하더라도 not getway가 출력됩니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="webhook">Webhook<a class="hash-link" href="#webhook" title="제목으로 바로 가기">​</a></h3><p>Webhook으로 jenkins가 trigger되면 <code>chmod +x ./deploy.sh</code> 으로 실행권한을 주고 <code>./deploy.sh</code> 으로 shell script를 실행합니다.</p><p>해당 script가 하는 action는 blue가 up이면 green실행 아니면 반대가 전부입니다.</p><p>shellscript에 대한 자세한 설명은 <a href="https://bhsbhs235.github.io/jenkins/springboot/docker/2020/02/02/dockercomposespringboot.html" target="_blank" rel="noopener noreferrer">docker-compose,springboot무중단배포</a> 을 참고합니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="reference">Reference<a class="hash-link" href="#reference" title="제목으로 바로 가기">​</a></h2><ol><li><a href="https://velog.io/@swchoi0329/NGINX-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC" target="_blank" rel="noopener noreferrer">추후, spring으로 무중단 배포시 살펴보기</a></li></ol>]]></content>
        <category label="docker" term="docker"/>
        <category label="Blue-Green" term="Blue-Green"/>
        <category label="DevOps" term="DevOps"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[nginx를 이용해 Load Balancer 구현 해보기 (with docker)]]></title>
        <id>/2020/11/29/implement-load-balancer-using-nginx-with-docker</id>
        <link href="https://parkgang.github.io/blog/2020/11/29/implement-load-balancer-using-nginx-with-docker"/>
        <updated>2020-11-29T03:00:00.000Z</updated>
        <summary type="html"><![CDATA[nginx를 docker 컨테이너로 실행하여 로드밸런싱 하는 방법에 대해 소개합니다.]]></summary>
        <content type="html"><![CDATA[<p>nginx를 docker 컨테이너로 실행하여 로드밸런싱 하는 방법에 대해 소개합니다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p><a href="https://sang12.co.kr/215/%28Docker%29Nginx%EC%99%80-Tomcat%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%A1%9C%EB%93%9C-%EB%B0%B8%EB%9F%B0%EC%8B%B1-%EB%B0%8F-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0" target="_blank" rel="noopener noreferrer">(Docker)Nginx와 Tomcat을 활용한 로드 밸런싱 및 무중단 배포하기</a> 해당 글을 참고하였습니다.</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="docker-compose-작성">docker-compose 작성<a class="hash-link" href="#docker-compose-작성" title="제목으로 바로 가기">​</a></h2><p>먼저 로드 밸런싱을 위한 <code>nginx</code> server와 test를 위해 <code>tomcat</code> server를 2개 띄어보겠습니다.</p><p>편리하게 실행하기 위해 <code>docker-compose</code> 를 작성합니다.</p><div class="language-yml codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">docker-compose.yml</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-yml codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">version</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3.8"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">nginx</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">container_name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">restart</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> always</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token datetime number" style="color:#36acaa">80:80</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ./nginx/nginx.conf</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/etc/nginx/nginx.conf</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">tomcat1</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> tomcat</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">container_name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> tomcat1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> 10001</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">8080</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ./tomcat1/webapps/</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/usr/local/tomcat/webapps/ROOT</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">tomcat2</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> tomcat</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">container_name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> tomcat2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> 10002</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">8080</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ./tomcat2/webapps/</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/usr/local/tomcat/webapps/ROOT</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="배치-파일-추가">배치 파일 추가<a class="hash-link" href="#배치-파일-추가" title="제목으로 바로 가기">​</a></h2><p>위에서 작성한 <code>docker-compose</code> 를 보면 볼륨 마운트가 되어있습니다.</p><p><code>nginx</code> 의 경우 config를 쉽게 변경하기 위해 <code>tomcat</code> 의 경우 로드 밸런싱의 결과를 확인하기 위해 정적 파일을 쉽게 추가하도록 마운트 되어있습니다.</p><p><code>nginx.conf</code> 와 같이 파일을 마운트 하는 경우 해당 경로에 파일이 없으면 아래와 같이 오류가 출력됩니다. 먼저 실행 전에 해당 파일을 작성하도록 하겠습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAh0lEQVQImWXOQQrDIBAFUC+gIEjCWKcJRvEE6iqLFNvc/0S/qHTVxeMzf2AYcX9uvFvD67rQWsN5nqi1opSCUsvInDPE9tywLAu01lBKTVJBSjn8OpFSQggB/vDY933ocxdjHLz3ECnGuTgC+nXnHJgZ7HgmM6y1EA8ikLUgIqzrOt4wxvz5ArP1RS+aZEREAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="979" height="512"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.c0126fe.979.png" srcset="/assets/ideal-img/1.c0126fe.979.png 979w" width="979" height="512"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="하이어라키">하이어라키<a class="hash-link" href="#하이어라키" title="제목으로 바로 가기">​</a></h2><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA0klEQVQImS3FPU+DQACA4ftNptqlpDHRSjkFoaVVG+AO6AEBMWr8rCS6GKOJDq5OLv7M16Q6PHnEy9s3r+8/dI9fXN9+UpkHSrNab7I7Li+eWd1/IEbbCTvWEYMNH6s3YdifM9z6159jbYYMelNEFnXk6gm96IhmN4T2cm3mGMJxzslhQ3p8hSgnDSY4pZi2FEGD8Qpy90/mFhi/wgQ1It1TLEcRZ05MPU5IdmNaR9FKxfl+Qu1oahkjPGnQUpFIjWtrQltjDlKU1JReysItqPyMX2oGfgNw8f7oAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1235" height="714"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.6eaa069.1235.png" srcset="/assets/ideal-img/2.6eaa069.1235.png 1235w" width="1235" height="714"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="source-code">Source Code<a class="hash-link" href="#source-code" title="제목으로 바로 가기">​</a></h2><div class="language-nginx codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">./nginx/nginx.conf</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-nginx codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token directive keyword" style="color:#00009f">user</span><span class="token directive">  nginx</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">worker_processes</span><span class="token directive">  </span><span class="token directive number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">error_log</span><span class="token directive">  /var/log/nginx/error.log warn</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">pid</span><span class="token directive">        /var/run/nginx.pid</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">events</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">worker_connections</span><span class="token directive">  </span><span class="token directive number" style="color:#36acaa">1024</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">http</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">include</span><span class="token directive">       /etc/nginx/mime.types</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">default_type</span><span class="token directive">  application/octet-stream</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">log_format</span><span class="token directive">  main  </span><span class="token directive string" style="color:#e3116c">'</span><span class="token directive string variable" style="color:#36acaa">$remote_addr</span><span class="token directive string" style="color:#e3116c"> - </span><span class="token directive string variable" style="color:#36acaa">$remote_user</span><span class="token directive string" style="color:#e3116c"> [</span><span class="token directive string variable" style="color:#36acaa">$time_local]</span><span class="token directive string" style="color:#e3116c"> "</span><span class="token directive string variable" style="color:#36acaa">$request</span><span class="token directive string" style="color:#e3116c">" '</span><span class="token directive"></span><br></span><span class="token-line" style="color:#393A34"><span class="token directive">                      </span><span class="token directive string" style="color:#e3116c">'</span><span class="token directive string variable" style="color:#36acaa">$status</span><span class="token directive string" style="color:#e3116c"> </span><span class="token directive string variable" style="color:#36acaa">$body_bytes_sent</span><span class="token directive string" style="color:#e3116c"> "</span><span class="token directive string variable" style="color:#36acaa">$http_referer</span><span class="token directive string" style="color:#e3116c">" '</span><span class="token directive"></span><br></span><span class="token-line" style="color:#393A34"><span class="token directive">                      </span><span class="token directive string" style="color:#e3116c">'"</span><span class="token directive string variable" style="color:#36acaa">$http_user_agent</span><span class="token directive string" style="color:#e3116c">" "</span><span class="token directive string variable" style="color:#36acaa">$http_x_forwarded_for</span><span class="token directive string" style="color:#e3116c">"'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">access_log</span><span class="token directive">  /var/log/nginx/access.log  main</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">sendfile</span><span class="token directive">        </span><span class="token directive boolean" style="color:#36acaa">on</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">#tcp_nopush     on;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">keepalive_timeout</span><span class="token directive">  </span><span class="token directive number" style="color:#36acaa">65</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">#gzip  on;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 여기 아래부터 기본 값 수정했습니다</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># include /etc/nginx/conf.d/*.conf; (제거하지 않으면 설정해도 기본 설정을 타게 됩니다)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">upstream</span><span class="token directive"> tomcat-project</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token directive"> 52.231.50.84:10001</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token directive"> 52.231.50.84:10002</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">listen</span><span class="token directive"> </span><span class="token directive number" style="color:#36acaa">80</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">location</span><span class="token directive"> /</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token directive keyword" style="color:#00009f">proxy_pass</span><span class="token directive"> http://tomcat-project</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="language-nginx codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">실제 default nginx.conf</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-nginx codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token directive keyword" style="color:#00009f">user</span><span class="token directive">  nginx</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">worker_processes</span><span class="token directive">  </span><span class="token directive number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">error_log</span><span class="token directive">  /var/log/nginx/error.log warn</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">pid</span><span class="token directive">        /var/run/nginx.pid</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">events</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">worker_connections</span><span class="token directive">  </span><span class="token directive number" style="color:#36acaa">1024</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">http</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">include</span><span class="token directive">       /etc/nginx/mime.types</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">default_type</span><span class="token directive">  application/octet-stream</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">log_format</span><span class="token directive">  main  </span><span class="token directive string" style="color:#e3116c">'</span><span class="token directive string variable" style="color:#36acaa">$remote_addr</span><span class="token directive string" style="color:#e3116c"> - </span><span class="token directive string variable" style="color:#36acaa">$remote_user</span><span class="token directive string" style="color:#e3116c"> [</span><span class="token directive string variable" style="color:#36acaa">$time_local]</span><span class="token directive string" style="color:#e3116c"> "</span><span class="token directive string variable" style="color:#36acaa">$request</span><span class="token directive string" style="color:#e3116c">" '</span><span class="token directive"></span><br></span><span class="token-line" style="color:#393A34"><span class="token directive">                      </span><span class="token directive string" style="color:#e3116c">'</span><span class="token directive string variable" style="color:#36acaa">$status</span><span class="token directive string" style="color:#e3116c"> </span><span class="token directive string variable" style="color:#36acaa">$body_bytes_sent</span><span class="token directive string" style="color:#e3116c"> "</span><span class="token directive string variable" style="color:#36acaa">$http_referer</span><span class="token directive string" style="color:#e3116c">" '</span><span class="token directive"></span><br></span><span class="token-line" style="color:#393A34"><span class="token directive">                      </span><span class="token directive string" style="color:#e3116c">'"</span><span class="token directive string variable" style="color:#36acaa">$http_user_agent</span><span class="token directive string" style="color:#e3116c">" "</span><span class="token directive string variable" style="color:#36acaa">$http_x_forwarded_for</span><span class="token directive string" style="color:#e3116c">"'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">access_log</span><span class="token directive">  /var/log/nginx/access.log  main</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">sendfile</span><span class="token directive">        </span><span class="token directive boolean" style="color:#36acaa">on</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">#tcp_nopush     on;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">keepalive_timeout</span><span class="token directive">  </span><span class="token directive number" style="color:#36acaa">65</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">#gzip  on;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">include</span><span class="token directive"> /etc/nginx/conf.d/*.conf</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="language-html codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">./tomcat1/webapps/index.html</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-html codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token doctype punctuation" style="color:#393A34;font-style:italic">&lt;!</span><span class="token doctype doctype-tag" style="color:#999988;font-style:italic">DOCTYPE</span><span class="token doctype" style="color:#999988;font-style:italic"> </span><span class="token doctype name" style="color:#999988;font-style:italic">html</span><span class="token doctype punctuation" style="color:#393A34;font-style:italic">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">html</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">meta</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">charset</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">UTF-8</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">title</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">Document</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">title</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">body</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tomcat1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">body</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">html</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="language-html codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">./tomcat2/webapps/index.html</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-html codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token doctype punctuation" style="color:#393A34;font-style:italic">&lt;!</span><span class="token doctype doctype-tag" style="color:#999988;font-style:italic">DOCTYPE</span><span class="token doctype" style="color:#999988;font-style:italic"> </span><span class="token doctype name" style="color:#999988;font-style:italic">html</span><span class="token doctype punctuation" style="color:#393A34;font-style:italic">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">html</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">meta</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">charset</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">UTF-8</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">title</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">Document</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">title</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">body</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tomcat2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">body</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">html</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="실행">실행<a class="hash-link" href="#실행" title="제목으로 바로 가기">​</a></h2><p>해당 파일 경로로 이동하여 <code>docker-compose up -d</code> 으로 실행하도록 합니다.</p><div class="admonition admonition-tip alert alert--success"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</h5></div><div class="admonition-content"><p><code>docker-compose</code> 를 실행하는 디렉토리명이 -p 이름이 됩니다. 때문에 디렉토리명을 -p 으로 작성하면 편리합니다.</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="확인">확인<a class="hash-link" href="#확인" title="제목으로 바로 가기">​</a></h2><p>새로고침 결과 정상적으로 tomcat1과 tomcat2가 스위칭 되는것을 확인할 수 있습니다.</p>]]></content>
        <category label="docker" term="docker"/>
        <category label="nginx" term="nginx"/>
        <category label="load balancer" term="load balancer"/>
        <category label="DevOps" term="DevOps"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Docker in Docker를 이용해 CI/CD 구축하기 (with jenkins, slack)]]></title>
        <id>/2020/11/28/building-ci-cd-using-docker-in-docker-with-jenkins-slack</id>
        <link href="https://parkgang.github.io/blog/2020/11/28/building-ci-cd-using-docker-in-docker-with-jenkins-slack"/>
        <updated>2020-11-28T04:00:00.000Z</updated>
        <summary type="html"><![CDATA[docker in docker 테크닉을 이용해 docker로 실행된 jenkins 환경에 또 docker를 이용해 웹 애플리케이션을 띄어보도록 하겠습니다.]]></summary>
        <content type="html"><![CDATA[<p>docker in docker 테크닉을 이용해 docker로 실행된 jenkins 환경에 또 docker를 이용해 웹 애플리케이션을 띄어보도록 하겠습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="jenkins-설치">jenkins 설치<a class="hash-link" href="#jenkins-설치" title="제목으로 바로 가기">​</a></h2><p>docker를 이용하여 jenkins를 설치합니다.</p><p>jenkins가 docker 컨테이너에서 돌아가고 빌드 유발시 컨테이너안에서 또 하나의 컨테이너가 생성되기 때문에 docker in docker 방식으로 구현됩니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="dockerfile-작성">Dockerfile 작성<a class="hash-link" href="#dockerfile-작성" title="제목으로 바로 가기">​</a></h3><p>기본적으로 jenkins에는 <code>docker</code> 와 <code>docker-compose</code> 가 없습니다. 초기에 쉽게 설정할 수 있도록 <code>Dockerfile</code> 을 작성하도록 합니다.</p><div class="language-docker codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">Dockerfile</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-docker codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token instruction keyword" style="color:#00009f">FROM</span><span class="token instruction"> jenkins/jenkins:lts</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">USER</span><span class="token instruction"> root</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">RUN</span><span class="token instruction"> apt-get update</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># docker install</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">RUN</span><span class="token instruction"> curl -fsSL https://get.docker.com/ | sh</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># docker-compose install</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">RUN</span><span class="token instruction"> curl -L </span><span class="token instruction string" style="color:#e3116c">"https://github.com/docker/compose/releases/download/1.25.4/docker-compose-$(uname -s)-$(uname -m)"</span><span class="token instruction"> -o /usr/local/bin/docker-compose &amp;&amp; </span><span class="token instruction operator" style="color:#393A34">\</span><span class="token instruction"></span><br></span><span class="token-line" style="color:#393A34"><span class="token instruction">    chmod +x /usr/local/bin/docker-compose</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>jenkins docker image</h5></div><div class="admonition-content"><p><code>jenkins</code> 도 있지만 2.60.3 이후에 더 이상 업데이트가 종료되었습니다. <code>jenkins/jenkins</code> 는 최신 jenkins 버전을 지원합니다. <code>jenkins/jenkins</code> 는 Jenkins의 기본 이미지만을 제공하기 때문에, 추가적인 작업이 필요하다면, 따로 Dockerfile을 세팅해야 합니다.</p></div></div><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>jenkins 사용시 마운트 이유</h5></div><div class="admonition-content"><p><code>/var/run/docker.sock</code> 파일만 공유를 하면 Local에 설치된 Docker를 컨테이너 내부에서 사용할 수 있고, 또한 컨터에너 내부에 설치된 Docker를 다른 컨테이너 내부에서 사용이 가능해집니다. 덕분에, 젠킨스 파일을 모두 백업할 수 있습니다.</p><p>자세한 설명은 <a href="https://medium.com/dtevangelist/docker-in-docker-fb54252e3188" target="_blank" rel="noopener noreferrer">Docker in docker</a> 를 참고허세요.</p></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="docker-compose-작성">docker-compose 작성<a class="hash-link" href="#docker-compose-작성" title="제목으로 바로 가기">​</a></h3><p>이미지 빌드 및 파라미터를 문서화 할 수 있도록 <code>docker-compose</code> 를 작성합니다.</p><div class="language-yml codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">docker-compose.yml</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-yml codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">version</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3.8"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">jenkins</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">build</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> jenkins/jenkins</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">restart</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> always</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">container_name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> jenkins</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">user</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> root</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> 8080</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">8080</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ./jenkins_home</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/var/jenkins_home</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /var/run/docker.sock</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/var/run/docker.sock</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">environment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">TZ</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Asia/Seoul"</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><ol><li><a href="https://freedeveloper.tistory.com/181" target="_blank" rel="noopener noreferrer">단축어와 같은 상세설정</a></li><li><a href="https://gwonsungjun.github.io/articles/2019-04/jenkins_tutorial_1" target="_blank" rel="noopener noreferrer">체계적인 Dockerfile 관리</a></li></ol></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="실행">실행<a class="hash-link" href="#실행" title="제목으로 바로 가기">​</a></h3><p><code>docker-compose</code> 의 파일 경로로 이동하여 <code>docker-compose -p jenkins up -d</code> 으로 실행하도록 합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAWklEQVQImXWKywqAMAwE8/+/6UkQaxpITB9ZaYWCBwcWlmFo20+YClQN7g4zQykVrTXUWtF7x+0FlESRmSEiU47gjcv8ww0o84Hr4iX+oMQMVV1hRHw26BF4ABVTnQ5FwtWtAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="926" height="428"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.cb3551f.926.png" srcset="/assets/ideal-img/1.cb3551f.926.png 926w" width="926" height="428"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAxElEQVQImR2Hy06DQAAA96uaWA81plqEhVpLFUmFXR7LKthgm6p9hHg0/vQYOExmRuy2vxz3fxyaH77rjtaeseaMLU9UxXHo3uJBNixlw2Ka417G3F5ETEaPXI9jZleKyehpQLzpDrM+EMlXwjtD6JSsnIJIWl4W76yGLxGfZUebfJEHFUVgMPPe1fCZb4buLfbZid3zllZqaleR3iRsXMXG03zIlNrLaKRCLOc1RVCQeBp/pgkdRelnpJ6iCjSxb7D3Of/4SX3p4v/+OAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1235" height="714"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.6a2c7e0.1235.png" srcset="/assets/ideal-img/2.6a2c7e0.1235.png 1235w" width="1235" height="714"></noscript></div></div><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>windows에서 설치하면 아래의 사진처럼 WSL이 가능하므로 Docker Desktop for Windows으로 설치하라고 많은 Warning message가 출력되는데 무시하고 좀 기달리면 정상적으로 설치됩니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA0ElEQVQImQXB3U6DMACAUZ5KvXLGJWZDWgSCAxdd2kL5nTIFcRiXeGOixnjhA3+e40z7Xw5vf4zjD/3TJ0P/xfPwza77oC4PFPkrNptwIrHl2msJLyxyrnFnd1yebVic3jI/STg/ipkdr3B00mOTARN36OiBTbDlRtakXknq5qQLQ7zMce6jmjasaKOGOqypwpoiqLB+SSktmSxQwuI8yoxRGN6DjL1vaJaKSRpGqXmRip0wdELhrGRJKTS5NMSuYe1qGj9DeYpCataioLoy/AP+QH+IS2wq6gAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1235" height="714"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.71a8098.1235.png" srcset="/assets/ideal-img/3.71a8098.1235.png 1235w" width="1235" height="714"></noscript></div></div></div></div><div class="admonition admonition-tip alert alert--success"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>파일 관리 전략</h5></div><div class="admonition-content"><p>현재 jenkins 컨테이어의 볼륨은 호스트 디렉토리와 바인딩 되어있습니다.<br>
<!-- -->앞으로 생길 docker resource를 쉽게 관리하기 위해 하나의 디렉토리에 <code>Dockerfile</code> , <code>docker-compose</code> , <code>config</code> 파일을 관리하도록 합니다.</p><div class="codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-text codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">// Ex</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> docker</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> └ jenkins</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ㅤ└ Dockerfile</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ㅤ└ docker-compose.yml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ㅤ└ jenkins_home</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> └ mysql</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ㅤ└ Dockerfile</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ㅤ└ docker-compose.yml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ㅤ└ config 파일</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="확인">확인<a class="hash-link" href="#확인" title="제목으로 바로 가기">​</a></h3><p>실행한 컨테이너로 들어가 <code>docker</code> 와 <code>docker-compose</code> version을 출력하여 정상적으로 설치된 것을 확인할 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA10lEQVQYlTXH3U7CMABA4b6WCpGxiQxG54QIrGvXDtZKNIL83Xnjix+TqRcn+Y44vn1zCF9szQVbnrDqRG2u1PrSZc0Vo86IxXRHkTQUA00+MEz6FcM7RXRbMrgpifuaYa9CqMUndfHBRu5YPm55Shx54pCxoxg1f7aIVepZTTwvacsybTuvp571xKNmgXIaOgsvW7z0BOl5zT27PHT/Xyt/X7TjmuPMcZaO98xhRoZDZjnJhn3muEjHftYgTKTQQ8U80sQ9xfxesXkwVLHGxiVhXPOcbPgBaWiPGZYKmR4AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1366" height="984"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.d47f1a0.1366.png" srcset="/assets/ideal-img/4.d47f1a0.1366.png 1366w" width="1366" height="984"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="jenkins-접속">jenkins 접속<a class="hash-link" href="#jenkins-접속" title="제목으로 바로 가기">​</a></h2><p>호스트 PC에서 <code>8080</code> 으로 접속하도록 설정했음으로 <code>http://{Server IP}:8080</code> 으로 접속하도록 합니다.</p><p>그러면 비밀번호를 요구하는데 비밀번호는 <code>docker-compose logs</code> 혹은 해당 컨테이너 logs에 적혀있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA70lEQVQYlU2OwUrDQBRF5zOVQkxqm0r9DbeCW7d1I66MomIySRausigBN8WZDCFRpAoGk4bUmczkySsuPHBX977DIyPbOp3Mjj5dd86ms2PhTKbCsQ+FM3bFgT3mluWs9/ZH5yQMw0vGGNR1DVVVQdu2oJQCKSX0fQ9CCIjj+IH4vr/IsgzazUat3z/M91dl5PbHIACgUBIEgYfDC845XiOAvdZ6ZzXGaMY4UEpvdkYcAoAchkFjcNA0je66Tv4Zrwml9Ar/+A8aMQh2URQ9Es/zTpIk4WVZLouiSDF5nqdvr2X6vOJLP356ub+7PfsFlbzpemwI3HcAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.6f94582.2956.png" srcset="/assets/ideal-img/5.6f94582.2956.png 2956w" width="2956" height="2222"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA7UlEQVQImS3K20rCAACA4T1KdISOq1FbDJOFNEI0tOOgVwq6KUi7q0zZA9hFouELFHaCoouakWtubWWHLfiD8Oq7+YSdU4vcWYt83WG/7pCvNdmrNtmtWOQqT2yfPLJVthDePYdO8EYU/vBwf0fW2GRpzSCzbpBe3UBPZ0hlVxCeWy6vbZ+v74jzxg2zWhJpTmcitsCgpNIzIjGmxBFe7DauFxCGv1xe3ZJILqMmUkxri4jqPKMzMWRN70bX/48XjWuG5Th9okK/qDAwqdA7LjM0pSB4fsBH55MwjLBth0LJ5KBQ5PC41NXkqGjyBwMKpszuQVM4AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1250" height="720"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.eb7c39c.1250.png" srcset="/assets/ideal-img/6.eb7c39c.1250.png 1250w" width="1250" height="720"></noscript></div></div><p>필수 플러그인을 설치하도록 합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA9ElEQVQYlTXOT2uDMBgGcL9txxR1bQf9RDvtttNOwaSgrDtsjC1eNNq1Ikjin0hDTeM7IuyBB97Dw4/XWXnui+eH3Wa7q9bbXR2sN3UQPNRB+Fh7fli5rt+u7u5fnSSJk6IoQF0uMEq5dJ5nMMaAzfFYQhzHH04URfuCMeC91OeGm4ZzI6U00zTZ5S3LMogifHAwxnsrMi5vn2cBv00L1ekEXAgLmjzPAWP8boeEMQbazFqqq1HXadGEEEYppa2IMX5zCCGHsiyXf/5zMwa01sttEULIl4MQeqKUsmEYvruup33f07Ztadd1dBzHnzRNc4TQ8x/Slu9RvgmgugAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2868" height="2134"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.3f8b0aa.2868.png" srcset="/assets/ideal-img/7.3f8b0aa.2868.png 2868w" width="2868" height="2134"></noscript></div></div><p>admin 계정을 설정합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA5ElEQVQYlXWMMU/DMBCF/TNBldokRClS/wYrEitrWRATAQGKE6cLAxmqDEzgmNgbYiiiUaw02Hfoqq486enp3X16bBJMz+P56VcyX7zHyUIFJ7GKoliFYaJmQSSns/Dz6HhyyYQQ11JK7Pset9sf7LoOrbXonENS27ZYluUT41m2bJqGnuNm8+2ttX4YBo+I5FEphXmep4xzfkWLAPA77Mb9CgkAKNwBvCNwSSAi7gDAee8dJZluB/CWFUVxQ+U/aa1RCJGxNE3PqqqSxpi1MaYma2Pqj1bX5cvrmq+e3x4f7i/+ABf36jNmBWSpAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.d56ce3b.2956.png" srcset="/assets/ideal-img/8.d56ce3b.2956.png 2956w" width="2956" height="2222"></noscript></div></div><p>jenkins의 TCP 포트를 앞으로 계속 같은 포트로 쓸 것인지 물어봅니다. 여기서 현재의 포트를 사용한다고 하면 jenkins 호출시 현재 포트번호로 호출해야합니다. 일단은 같은 포트를 사용할 예정이므로 Save하여 사용하도록 합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA5klEQVQYlXWMT0vDQBTE92MqhbapsRH6NbwKXr3Wi3gy4h82yeZiLzmE3gTdbJJVkRKLxOq2dN3dJ69U8OLAMO8NP4Z0+t0jPzhohsHo0Q9GwvP3xd7AF543FL3+gHd73mxnt3NCGGNnnHNQSsFi8Qlt+wFfSoG1FlBVVUGapreEUjouigJWq6Vu3ub2ddbY+XtrtdZI6rIsIY7jkERRdIqLAPDtnNusYBpj8DRb8BLB8RZcO+fMX2MnhEDwgiRJco7Pf6rrGhhjlIRheJhlGZdS5lLK6a9fnp+mk/w+p+ndw8311fEP3VXp/FNhLU4AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9.da8d1d2.2956.png" srcset="/assets/ideal-img/9.da8d1d2.2956.png 2956w" width="2956" height="2222"></noscript></div></div><p>이로써 docker를 이용하여 jenkins 설치는 끝났습니다. 이제 ci/cd를 위한 소스코드를 작성하도록 하겠습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA5klEQVQYlXWMzWrCQBSF52XEDikIVbFPIVT6SG4Lbt3qRly1hZqkpRRdBiRrHUc3/jS9HTJEY03HmSujUlc98HF/zj2XFCvlh1Ll9stxCmPqFHguT/lVnvJqtcbvavfsplj+pPS6SXzP7QRBgP3BAHuejy89F9/eP3C+WKKUEsMwRM9/dQkAtPCk31WcarHZ6T2iVga13VlDKfVEJtNZe5Nu7aDOAURjjmit98YYFEI8kwmfttbpFn92WWaNE8Ye2D6zuTiOH4mUsvv36R8lSeITznldCMEAYAgA4YVvW4dRFI0YY40DPeTrw/UxTeUAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/10.cbe5cf9.2956.png" srcset="/assets/ideal-img/10.cbe5cf9.2956.png 2956w" width="2956" height="2222"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="소스코드-작성">소스코드 작성<a class="hash-link" href="#소스코드-작성" title="제목으로 바로 가기">​</a></h2><p>실습이 진행된 코드는 <a href="https://github.com/parkgang/helloworld-docker-cicd" target="_blank" rel="noopener noreferrer">parkgang/helloworld-docker-cicd</a> 에서 확인하실 수 있습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="webhook-설정">Webhook 설정<a class="hash-link" href="#webhook-설정" title="제목으로 바로 가기">​</a></h2><p>GitHub에서 소스코드 변화가 감지되면 알아서 빌드되도록 Webhook을 설정해야합니다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p><a href="https://medium.com/hgmin/jenkins-github-webhook-3dc13efd2437" target="_blank" rel="noopener noreferrer">GitHub에 push시 Jenkins Webhook 연동 방법</a> 글을 참고 하였습니다.</p></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="github에-webhooks-설정">GitHub에 Webhooks 설정<a class="hash-link" href="#github에-webhooks-설정" title="제목으로 바로 가기">​</a></h3><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAxklEQVQImW2MwWrCQBRF54daNabWFhf6Oy78lypd5EtEEOlKF2FacfLS2kzyEYkwksx7V6a0u144HLhwr5pMZ6/jyTSPHp7T4ehJD6KR7g0Csb7rD9P7fkxR/JioVB/fPswnTlmAcDIZTJYj/zrDmAxEOfT7ca+87zYA4JlbACwiHPxL6OC936mmadZt26JuLlfP3IlIx8x/XEUEdV1vlXPuEFadDwf/xzmnFRHNq6pKrLUv1parwpar78L+uCjssiyrhIgWNyjvxBnWNi7DAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3808" height="2242"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/11.d79ef92.3808.png" srcset="/assets/ideal-img/11.d79ef92.3808.png 3808w" width="3808" height="2242"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA20lEQVQImVWMO07DQBiE90R5OMHIyAW5DgV3AYsiJ0FICNFACmtjsL12miyBjsaI2Mtv78ODlo6Rppjvk4bF56ubKF5VwclZuggjPg9CPgtCPp0v+WS6SCezpTiN4jVL+ctjVtTIyxpltcNrIZAXAqXwrEJV78C32TOz1twBwAho3Q+Ovo/Oae1GwAHQ3hHRA2vb9nYYBrSKenX8Mqr5NFYr4ywZa3/6cXRomuaeEdHm73EEaOihSPn5L13XcSaEuJBSrj8O71dP203C8yx5k4dkv5e+194JIS5/AdKXwwaloIyJAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3808" height="2242"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/12.8cffdc8.3808.png" srcset="/assets/ideal-img/12.8cffdc8.3808.png 3808w" width="3808" height="2242"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="jenkins-item-생성">jenkins item 생성<a class="hash-link" href="#jenkins-item-생성" title="제목으로 바로 가기">​</a></h3><p><code>~\jenkins_home\workspace</code> 에 프로젝트 파일단위가 저장됩니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA50lEQVQYlXWMzWoCMRSF8zLFhhEEq9anEBQfyW3BrVvdiKuq+DMVKbqpCDLrGmM3bW1Mw4TRUacxuSVa6soDH/fn3HNRKpt5SN1n146TmGMnQW9imN7GMM3lCjRfKJLkXfoL43gFue1mfTp5gdF4DN2+C51uDwbDZ3j/+AQpJXieB333qYc4Y1U462flh1psD/oIoJUBbXfWUEo10WL5VtuGOzuovwCAMSe01kdjDAghWmhBl9VNuIP9IYqsccbYA9tHNuf7/iOSUjb+P11REAQuopSWhBCEcz7jnHsXvm2dMcZeCSHlXy7u67LMqx5IAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/13.d527319.2956.png" srcset="/assets/ideal-img/13.d527319.2956.png 2956w" width="2956" height="2222"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAABCklEQVQYlR2Nz0uEQBzF548J3IItrK3ov6j20D/UNejadbtEp4okQlmli5AsnmuxbcpBzXXGH6Po7Og39PDhwXuP99DB0eTq8PjkbzxWP5XtsbeljLyRsuOdTy+807PpcndPDdX9yQ0y5/M727ZB0zTQdR0MwwDTNIEQMuA4DliW9YKEEDMAAEoT0TRN23XdQFmWLedc9Bnn/BElSXIrhADf9zdxHEOapsAYg6IooKoq2bYthGH4hBhjM7GR4JOgWa9jyRiTlFKZZZnknDf9YhRFDyjP8/sOAHxCgFLa30CWZYPWdd33ev8V+b8/l0EULZ/f3her1ZeLMXa/MXYJIW4QBAtK6QfG+PofEmzoFQSnU7sAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/14.c56f83c.2956.png" srcset="/assets/ideal-img/14.c56f83c.2956.png 2956w" width="2956" height="2222"></noscript></div></div><p><strong>public repose이므로 GitHub 계정은 입력하지 않았습니다.</strong></p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA60lEQVQYlT2OwUrEMBCG86rrtnYrBX0Ir4JXr3oRT+pFxAfwtOxZlx7WdWsySTNppC1J046kyA58/AzM/zEszbOr4vwCslXxuUzzcpGclmmalctkVS5Okm1+VvAkyW4YIt4PIZC1lrz35Lyf0/uBnHMUp2maF2YQb0MIcfGIejQSZn5rNRqNPowjKSmfovEuHhpjBiklKQCqpSQpBIEQYZomAoBnpv+NiOgAIIDgAUEEFDzIqnJjNCr1yKy1D7Gltaa2banv+yNd180/Wmtf2eF7f7mv+PbtfbOuqsNGCHHkh/O1VPXH1253/QeB+PMAW3MqPgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/15.b32edb0.2956.png" srcset="/assets/ideal-img/15.b32edb0.2956.png 2956w" width="2956" height="2222"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA80lEQVQYlT2Ov07DMByE/aq0SdOWf+IdWJFYWWFBTMCCEC/QpSorVJFSJREkwnac1MnPjp1DKRLDpxvu0+lYuIiuTs4vyvni7HM6O44nwSwOwyieBvP4aBJsl8vT7yCIbpisqvve9uBFAV3XMMYcsNagI4IbgLpWL6yqqtu+78HLwmqlPBH907addc5DSvnEpJR33nv8cN6rpgEZAyKCIRrT+WGAEOJ5FA+LknOjpXTdvnEdkRulfd2YcaQsy0emlHoAAKXU4WOrWxhrYa1Fq/VYgQvxyrI0vcy+iu3b6n29S9PNLs03ef5HludrIcRHkiTXv1Dq9G/KdQGDAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/16.65908e7.2956.png" srcset="/assets/ideal-img/16.65908e7.2956.png 2956w" width="2956" height="2222"></noscript></div></div><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>shell의 pwd는 <code>/var/jenkins_home/workspace/~</code> 입니다.</p></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="webhook-확인">Webhook 확인<a class="hash-link" href="#webhook-확인" title="제목으로 바로 가기">​</a></h3><p>먼저 강제로 trigger으로 스트립트와 GitHub pull하는데 문제가 없는지 확인합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA7klEQVQYlWWOzUrDQACE92GUVFrrJVX7Dnqo+EheBa9e60V6sSoYDEkuuYhYvOjBrGtRqHSbkG1+SJq6uyNBQcWBj4GZOQwxtzqHne3urNncCFYbLbZiNJhhrLGd3R7r7e1T09ycttfbx+TKsk5934fneXAcB7Ztw3VdcM6RJHPc3N5hcHZpkbcJ7+NLS6mhgD8s6+KVi3PCguCkXFQoFtVHludQSkNKCa01lFKyHiZzcUGeHx/6cThDkecVAKm1/k2dIY7FkEwn74Miy1CV5feD/0rT9Jq8jMcHsRA0iqJRGIb3P0S1jzjnT5TSo0/kWusNtJcghgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/17.9100db1.2956.png" srcset="/assets/ideal-img/17.9100db1.2956.png 2956w" width="2956" height="2222"></noscript></div></div><div class="admonition admonition-tip alert alert--success"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</h5></div><div class="admonition-content"><p>만약 GitHub repos가 권한이 필요한 경우 혹은 item이 잘못된 경우 Console Output을 통해 수정합니다.</p></div></div><p>문제가 없다면 소스 수정 후 GitHub에 Push하여 jenkins에서 자동으로 빌드가 유발되는지 확인합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA+0lEQVQYlU2NQUvDMBzF812E1VLEQ9X5IfQw8SN5Fbx6nRfxpF5soerF1V2GvWoJOJnLmjR2ictmmqR/mYr6g8d78OA9FLY3Dzfa2zQI1p9aq2t4peVhz/Pxzm4Hd/b28zDcmvh+cIziODpN0xSub24himLo9e6g378HSilMp1PIsgySJLlCozHpwje1Uso1DTgAcA6+vF4WxpgLNMT4ZLGYg9baEEJg9DoGQt+Aixmo+YdtnAPO+SV6GT53rbVQa63LCbFSCCvlu50pZY0xerlYVdU5EkKc/VwDQPMX/yGljBHG+IBznjPGBmVZPjDGfkUZGxRF8Zjn+dEny33qqdM7xOAAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/18.46866e6.2956.png" srcset="/assets/ideal-img/18.46866e6.2956.png 2956w" width="2956" height="2222"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="webhook이외-원격-빌드">Webhook이외 원격 빌드<a class="hash-link" href="#webhook이외-원격-빌드" title="제목으로 바로 가기">​</a></h2><p>token을 이용해서 API처럼 원격으로 빌드를 수행할 수 있습니다.</p><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p><code>http://kyungeun.koreacentral.cloudapp.azure.com:8080/job/helloworld-docker-cicd/build?token=rebuild_token</code></p></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA6klEQVQYlT3Ou07DMBTG8bwqxWkTFAkeghWpa9eyICZgAR6AMepcqgy5SHWcxHYs5cT1hYNcLkf6bUef/lGcru6y6xuWJNnnZZwWC7IslnFSEJIWFwtySK8ySshqEwkhHqy1yDnHCQBnrf8BzBhuHMfXSEq5tdZh3/dGCe5BqV+jn5Qy3nsUQjyHxXvnPHZdZ49NjX1LkXcMOWMoh8E5/xVGXsLj1liLA+enCcDNWp9prd0E88lYj4yxp0gp9Rg6QiMAYOj9Y4z5aZTyPWrq+rY5toe3jzyvqnJHKd21bXtGKc2Hge+rslx/A8vm89m2cTJhAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/19.18001f3.2956.png" srcset="/assets/ideal-img/19.18001f3.2956.png 2956w" width="2956" height="2222"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="소스코드-docker-컨테이너화">소스코드 docker 컨테이너화<a class="hash-link" href="#소스코드-docker-컨테이너화" title="제목으로 바로 가기">​</a></h2><p>이제 소스코드를 컨테이너화 시켜서 배포해보도록 하겠습니다. 소스코드는 <a href="#%EC%86%8C%EC%8A%A4%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1">Webbook때 사용한 코드와 동일합니다.</a></p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="execute-shell에-추가">Execute shell에 추가<a class="hash-link" href="#execute-shell에-추가" title="제목으로 바로 가기">​</a></h3><p>아래의 shellscript를 jenkins item의 Execute shell에 추가합니다.</p><p>컨테이너를 종료하지 않으면 같은 이름의 컨테이너가 실행되지 않음으로 삭제하고 다시 이미지 빌드 후 컨테이너화 하는 script입니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">rm</span><span class="token plain"> -f tomcat-cicd </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">docker-compose</span><span class="token plain"> -p jenkins-helloworld-docker-cicd up -d --build</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>위의 shellscript는 <code>docker-compose</code> 를 이용한 방법입니다. 아래의 script와 동일합니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">rm</span><span class="token plain"> -f tomcat-cicd </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> build -t tomcat-cicd-image:practice </span><span class="token builtin class-name">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> run -d --name tomcat-cicd -p </span><span class="token number" style="color:#36acaa">8888</span><span class="token plain">:8080 tomcat-cicd-image:practice</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA6klEQVQYlT2OvU7DMBSF86oFp01DhXgIViRW1rIgJmBBPETFGJUqgQnFVlLHPxfHsW8uciU40rd95+hkeVncXFxeiVWx+ThflvWCLes8L2rG1vXijB3W5aZlbHWXSSkfQgg0KEUAQM458t6fGMeRUpRSr9kg5TZipGPXTaAN/hhAZwG9cwjyOGGMJKV8Tov3iEhKyqAHRVppAmNpBCDQKs6ISXxJ4jbGSGoYvLEQjbERAE5YC36eZ+r7/imzRj9OEan5FhTCRKn0R/qe0nXdW/bZNNetEIeq/tpx3r4LIf7hnO/alu+rqrr9BUeE9Ly2GihlAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/20.aee4511.2956.png" srcset="/assets/ideal-img/20.aee4511.2956.png 2956w" width="2956" height="2222"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="확인-1">확인<a class="hash-link" href="#확인-1" title="제목으로 바로 가기">​</a></h3><p>push후 정상적으로 컨테이너가 실행되는 것을 확인할 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAlUlEQVQImWXKSwrCMBhF4ex/7kx8IN2FI7E+9iHapkmTP02aWgdHVISKg4/LhaNm2wvznWZZGhalpTi3bE4tq4OlOBrWZcNib1CdM4xJ6KXlkROMGcZ+4vPVTTuqxlLVBu8jXiI+JLxMhISqG4fRDfp6xdaW0Hpi7Om69ENV2uKcEJwgr5X4F71DJ4GUMsNwJ+ev4c8TAKzhS/Cu1CEAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1250" height="720"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/21.c8862a2.1250.png" srcset="/assets/ideal-img/21.c8862a2.1250.png 1250w" width="1250" height="720"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="slack-연동">slack 연동<a class="hash-link" href="#slack-연동" title="제목으로 바로 가기">​</a></h2><ol><li><a href="https://jojoldu.tistory.com/139" target="_blank" rel="noopener noreferrer">docker를 이용한 CI 구축 연습하기 (젠킨스, 슬랙)글을 참고합니다</a></li><li><a href="https://dnight.tistory.com/entry/Jenkins-Slack-%EC%95%8C%EB%A6%BC-%EC%97%B0%EB%8F%99" target="_blank" rel="noopener noreferrer">해당 글으로 성공했습니다</a></li></ol><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="docker-in-docker-issue">docker in docker issue<a class="hash-link" href="#docker-in-docker-issue" title="제목으로 바로 가기">​</a></h2><p>docker in docker으로 사용시 생기는 몇가지 문제점이 있습니다.</p><p>파일 마운트의 경로가 host pc으로 잡히는 문제가 있는 걸로 확인됩니다. 주의해서 사용바랍니다.</p><p>자세한 설명은 <a href="https://stackoverflow.com/questions/31381322/docker-in-docker-cannot-mount-volume" target="_blank" rel="noopener noreferrer">Docker in Docker cannot mount volume</a> 를 참고하세요.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="기타-빌드-도구-설정">기타 빌드 도구 설정<a class="hash-link" href="#기타-빌드-도구-설정" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="gradle">gradle<a class="hash-link" href="#gradle" title="제목으로 바로 가기">​</a></h3><p><a href="https://miiingo.tistory.com/171" target="_blank" rel="noopener noreferrer">jenkins에서 gradle shell 활성화</a></p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="yarn">yarn<a class="hash-link" href="#yarn" title="제목으로 바로 가기">​</a></h3><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><ol><li>yarn plugin이 없어서 npm을 통해 설치 후 사용하는 것으로 확인됩니다.</li><li>node script에 하면 안됩니다.</li></ol></div></div><ol><li><a href="https://dev.to/techworld_with_nana/how-to-configure-build-tools-gradle-yarn-in-jenkins-and-use-them-in-jenkinsfile-4eci" target="_blank" rel="noopener noreferrer">gradle + yarn 설정</a></li><li><a href="https://sg-choi.tistory.com/213" target="_blank" rel="noopener noreferrer">job 설정 부분 참고함</a></li></ol><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAABD0lEQVQYlSXNzUoCQQAH8HmYcKkIIj/oNUzoiboGXbvaJTpZIbZ4iF0oI5EFw6BsRAlb92NG3W12d8aZ/h38vcCPVI6r54dH5dCy9j939w7oTsmiVsmijZNTWq83xpVyZVGr1i6J/dC57vdf4bgu7js2uo8OnKdn+BFDEHN03R5abbtD5v6iiS3FE2Fy/Wc0YH4LadIiV0wo0G//lnzRyZXIMgiRb1arNZSUkEUBJQtkItVFJuD/zO/ImE6aqciRpIlkcaSFEFoqqY0xWikllVKIoqhF1uvVzXbW4CyClBKcM8RxDK030FqDc26TXu/lbDR6Hw+Hb4PpdOqFYegxxrwgCLzlcjlIkuRjNptd/AP/O+htvG73ogAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/22.93565d1.2956.png" srcset="/assets/ideal-img/22.93565d1.2956.png 2956w" width="2956" height="2222"></noscript></div></div><p><code>NodeJS</code> Plugin을 설치합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA9ElEQVQYlXWNS0vDQACE98/4LNHgo/Re8D/04q/xKnj1qhfxpEFot0fLCiI1BdPGdrs+LqVxs9kQGh9td0fiA08OfAwMwwzZqmzvuxubL4tLpcFKaV0sLK+KcrkiarVdUa3u8DXHHTuOe0gajfoJYwyXrRYopbjwPPh+BypJIIYCV4yBNpt1kuf5Eb41ve10zfWNb4JwYO6CngnC/nT48IReeH9O4jg+LlrZ5G32ONLoPkuMZAprZhirbD55fYdOlEeUUl+L1toPAPNfrLUFRQat9RlJ0/T05/pfZVlGiRBiTynFpZRtKaX/R1x4O4qiPuf84BPflOhOOOGOhwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/23.53739f3.2956.png" srcset="/assets/ideal-img/23.53739f3.2956.png 2956w" width="2956" height="2222"></noscript></div></div><p>설치 후 config에 들어갑니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAABEElEQVQYlSXNTUrDQBiA4TmMNKgIYtPiNWrBE7kV3LqtG3FVFYmhC0lAUiwli2pBY2qyKGYmnclPO0lmMukn2PcAz4v0087l8UmbaNrh1/7Bkb/X0nytpfn9s3O/1+t7eluPup3uNTKfjdvJ5A0s24ZHw4TRiwXWqwNRTAGvGIxsB4ZPpoGWv9EAdkmW86ZU20Zuocl42WxEJbOqgZ8lvkff/uKGFwVwXtZpmoEUFawIgSRhUJUb1dQSCI4ekOcvBmteQr7OBaWx4pyrouBKCKmklEIICSSOhyjL0rvdWQGjMQgh/jVKKShVg1IKGGMmcsbji4/53JvN3qdBELiEEJdS6mKM3SRJpnmef4ZhePUH5WjoPhecA4UAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/24.a56afc4.2956.png" srcset="/assets/ideal-img/24.a56afc4.2956.png 2956w" width="2956" height="2222"></noscript></div></div><p>node에 대한 이름과 버전을 선택합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA4UlEQVQYlT2MwUrDQBiE865m05iIT+FV8OrVXsSTehEfokS92FqWQtFQstnt2hSa/Xf/HdkiHRjmMB9fJqryuqwvOiHOv8SklnlRyjwvZFFU8iyfLKv6ciNEeZsZY+4BIMaYBkQEJgdwAEJIB7bGvCTw7h8kADyOI++Hgd3OstsPFInQK/WUwGkCOUafrMwM5xx+tcZWdcGNBxjdP5+MiNEBCN77QETHDczOfjRYv789Ztbah8R5jjhQgPf+aD1l2KFr29dMSnlljF5+t5vZav3TaN03SqWq1Flv7eJzPr/5AzvH9jAXFUfqAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/25.044f728.2956.png" srcset="/assets/ideal-img/25.044f728.2956.png 2956w" width="2956" height="2222"></noscript></div></div><details class="details_rxUF alert alert--info details_PYSW" data-collapsed="true"><summary>NodeJS script는 실제로 node.js로 js를 실행하기 위한 script만 가능한 것으로 확인됩니다.</summary><div><div class="collapsibleContent_oXwz"><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA6klEQVQYlU3Ov07DMBDHcb8qbZ1GKTwGIxJr17KgTvwZEDxBEYrCSICEBRxSI59zthO1sTmUFiFO+m4/nT6MJ/FJPDtc8yh55lFSjHhUjMe8mEzi4mDEX5LZ0Sfn0zkDgPMQAiEaUhoJGiRrHTnnyDpHwzVNc8O0hkXfe3p7l1sJXbCuDW27zyJuv0MgALgcPp5570lK2XddR8aYv1Br/zu8YkqpxTAUotqsv5RHxP9tBhYAXDBEXA4OACA0jtp279sZrd0ZlVK37H61On5Is9fl9V2a50+PdV1nQoisqqrsQ4gUAPKyLE9/AI0E8oGaiWJvAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/26.6ee5008.2956.png" srcset="/assets/ideal-img/26.6ee5008.2956.png 2956w" width="2956" height="2222"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA7UlEQVQYlT2OvU7DQBCE/aqAzw42vAUtEi1taBAV0KC8AF2UGiIXVsg5jn2398fZDnssuoAYaTTNp5lJWDG7npUX+ywr3lleVmcsr9I0q9L0vDo5ZeuivNwxlt8mAHAfQiCxb0lLSVbB0c5oMqKnKG3MSwJKzQMiyb47WOfCMI7Be/+b1h5CQAKAp9h4h4gEXfclhCQhgJRSZJQiLQR+hxDB5wjOI9i17WQU4Ke16J3DwXs0Ukxxre/7x0Rr/fD3g7R15PxA4zjROE3kh+H4UUq5SLYfm6sN360Xr6sl59tV0zT/5pwvAeCtruubH5iF835ezcNHAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/27.391be17.2956.png" srcset="/assets/ideal-img/27.391be17.2956.png 2956w" width="2956" height="2222"></noscript></div></div></div></div></details><p>아래의 체크박스를 선택 후 <code>Execute shell</code> 에 쉘을 작성합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA70lEQVQYlT3OvU7DMBQF4LwqbQxpC2/BisTKWhbEBCyoL8BWMpcqQ9o0lOu6jm9U/ySOL3KRuNIZjnT06SZsmt1ls2tgbPKVskkxYpfFeMyKNM2KixFbT2c3e8auHhIp5ZP3AymlSGtN1lpyzlHnHBlraQhEiPieNE0z996TEKI7aT1Ya/+jjekicjweX6P42Pc9fW82fXPgZy2qMcYYP4RAnMNbHM7j8ADgOPx4KaVHRK+U8ti2LoQQxZcEEZ9jwbY9a8aYvx+7jsxJUzxEXCS7anu7rffrxcfnsq53OQDPASAHzvO6qpZCiFVZlve/KuX0YncAvh0AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="2956" height="2222"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/28.c4557a6.2956.png" srcset="/assets/ideal-img/28.c4557a6.2956.png 2956w" width="2956" height="2222"></noscript></div></div><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>만약에 또 <code>yarn: not found</code> 출력된다면 node script만 실행가능한 item을 생성&amp;실행을 통해서 강제로 <code>yarn</code>만을 설치하도록 합니다.</p><p>깔끔하지 않은 방법이며 이럴꺼면 처음 docker image에서 yarn을 추가하는 것이 깔끔해 보입니다.</p><p><strong>추후 jenkins 재설치시 한번 더 확인하도록 합니다.</strong></p></div></div>]]></content>
        <category label="docker" term="docker"/>
        <category label="jenkins" term="jenkins"/>
        <category label="slack" term="slack"/>
        <category label="CI/CD" term="CI/CD"/>
        <category label="DevOps" term="DevOps"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Docker로 Tomcat 실행해보기]]></title>
        <id>/2020/11/28/running-tomcat-with-docker</id>
        <link href="https://parkgang.github.io/blog/2020/11/28/running-tomcat-with-docker"/>
        <updated>2020-11-28T03:00:00.000Z</updated>
        <summary type="html"><![CDATA[Docker를 이용해 빠르게 tomcat web server를 올려보도록 하겠습니다. 또한, 파일 마운트를 통해서 리소스가 바로 변하는 것을 확인할 수 있습니다.]]></summary>
        <content type="html"><![CDATA[<p>Docker를 이용해 빠르게 tomcat web server를 올려보도록 하겠습니다. 또한, 파일 마운트를 통해서 리소스가 바로 변하는 것을 확인할 수 있습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="docker-image-준비">Docker image 준비<a class="hash-link" href="#docker-image-준비" title="제목으로 바로 가기">​</a></h2><p>Docker 컨테이너를 올리기 위해선 image가 필요합니다. image를 만들기 위해서 <code>Dockerfile</code> 을 작성하도록 하겠습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="dockerfile-작성">Dockerfile 작성<a class="hash-link" href="#dockerfile-작성" title="제목으로 바로 가기">​</a></h3><p>먼저 파일 관리하기 용이하도록 호스트 PC에 <code>example</code> 디렉토리를 만든 후 내부에 <code>Dockerfile</code> 을 작성하도록 하겠습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAZklEQVQImU2M0QrCMBAE8/9f6KMvUrSkVdKkd03uRhKVOjAcuywXLteJvL1QPQDH3TA77TkXIdzuC/P8YMuF2oxmzi5K2RU96uhqbYRlfRJjHMGdr455/95lEFJKiMhn0fndP8ycN/93nN33338aAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="824" height="345"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.2fb98c7.824.png" srcset="/assets/ideal-img/1.2fb98c7.824.png 824w" width="824" height="345"></noscript></div></div><p>Dockerfile을 아래와 같이 작성합니다.</p><div class="language-docker codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">Dockerfile</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-docker codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 어떤 이미지를 사용할 것인가</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">FROM</span><span class="token instruction"> tomcat</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># cmd 실행</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">RUN</span><span class="token instruction"> apt-get update</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># [tomcat 데몬이 실행되고 나면 죽어버리는 것을 방지하기 위함](https://skaqud.github.io/2016/08/21/Build-Docker-Container/)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">CMD</span><span class="token instruction"> [</span><span class="token instruction string" style="color:#e3116c">"catalina.sh"</span><span class="token instruction">,</span><span class="token instruction string" style="color:#e3116c">"run"</span><span class="token instruction">]</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="이미지-생성">이미지 생성<a class="hash-link" href="#이미지-생성" title="제목으로 바로 가기">​</a></h3><p>아래 명령어를 통해 작성한 <code>Dockerfile</code> 를 이미지로 생성합니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> build -t tomcat-test:practice </span><span class="token builtin class-name">.</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="admonition admonition-caution alert alert--warning"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>caution</h5></div><div class="admonition-content"><p><code>Dockerfile</code> 이 존재하는 경로에서 실행되어야합니다.</p></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAzUlEQVQImSXDXUuDUACA4fOrGkHkLuZwm7odZJgfU0yPR8NBKs6pRd110Y9+u+iBR/TtD/Pwy635Zmq+GOpPruVCrRa0WqjUQlnMCNfSeE6Du8lxjAv7p5Dt4wvrlc/zg4+x+i+KaKRKFsrLzGs4kkcTmT8QyyuBU3G2FOedQrTJSJtONEFH5TVoWaNONcVRk7ua3CnJbIXoLzfu6Z2PuKPz3ij2Oe1R0UvNIBXvJ00rFcLeFaSHjOiQYRoB9jogNkP8TUCyDZFmQmqF/AGHa36bUxjFtgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1235" height="714"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.dad91f4.1235.png" srcset="/assets/ideal-img/2.dad91f4.1235.png 1235w" width="1235" height="714"></noscript></div></div><p>이미지가 생성된 것을 확인할 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAxUlEQVQImR3IXU+CUBzH8fOqarNGElNB+WO1kANN5fBwkIoxa91k1tZlb/rb4OKz34M6ffwxOL788N790rdn+vab/nCmtV809QlbfaLuZiX+9RP+Vcb9vEbcnLVXEE73eJMM52LDzWWC2qwOaHlGhy2Pfj2KA0u8tCQrSzzuGlVJTSl2VEUWGzWU0dCHbMbPiEVVy4ImyDmKoQsN+WJPHxp6KXgTw6uUdGJQi+mW1NUktylzJ0Uczc7L0K4mn6U8eDuKYMs/XEl7uReiOtQAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1235" height="714"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.d9e2ff8.1235.png" srcset="/assets/ideal-img/3.d9e2ff8.1235.png 1235w" width="1235" height="714"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="컨테이너-실행">컨테이너 실행<a class="hash-link" href="#컨테이너-실행" title="제목으로 바로 가기">​</a></h2><p>이제 생성된 이미지를 이용하여 컨테이너를 실행할 것입니다. 아래의 명령어를 입력합니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> run -p </span><span class="token number" style="color:#36acaa">8888</span><span class="token plain">:8080 tomcat-test:practice</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>실제로 tomcat만 실행시 컨테이너 내부의 <code>webapps</code> 에는 리소스파일이 없습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAUElEQVQImYXGMQrAIAwF0JDgIIKZ9CQJUTd7/zv9Qjs5OTx4tPeDORfcHWYGM/8eMTDGLyJAKSWIMETkwMwHUlXUWq+olIKc8xX13tFau3oBr7E92Oi9SwoAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="979" height="512"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.05cf98c.979.png" srcset="/assets/ideal-img/4.05cf98c.979.png 979w" width="979" height="512"></noscript></div></div><p>때문에 404를 출력하게 됩니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAApklEQVQImX3KuwrCQBQE0P1E44aQKCL4aFL4LyoW+RIRRKy0kC3XJt5EzWrsNy8i3iuJ2qRw4BTDDOsPR4vB2D1yq3docVtwsyPaNUcY3D4Y3JGm1fWYD+E2UjGdL4qu0Z3U7UGR+onr7p/CHUvTdEVEhIglIr4aympLkmTDtNbL77FAxGdD8TnqNcuybF+Vf8nzXDAppRsEgQcAUwCYN8yqTUo5eQMrYspGQ2C3RAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3808" height="2242"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.8ac1ceb.3808.png" srcset="/assets/ideal-img/5.8ac1ceb.3808.png 3808w" width="3808" height="2242"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="파일-마운트-컨테이너-실행">파일 마운트 컨테이너 실행<a class="hash-link" href="#파일-마운트-컨테이너-실행" title="제목으로 바로 가기">​</a></h2><p>이번에는 이를 방지하기 위해 호스트의 파일을 자동으로 Docker 컨테이너로 보내줄 수 있도록 파일 마운트를 해보도록 하겠습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="호스트pc에-리소스-생성">호스트PC에 리소스 생성<a class="hash-link" href="#호스트pc에-리소스-생성" title="제목으로 바로 가기">​</a></h3><p>먼저 컨테이너로 전송할 리소스가 필요합니다. 이전에 만들어둔 <code>example</code> 에 <code>resources</code> 디렉토리를 추가하겠습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAZUlEQVQImU2KQQ7CMAwE8//vceYEB9SqMpVriFM7g1opFSPtYWe33O5PzJTqjchkj7jS9iAiUauUx2thnidU11MefGvF7IO7k5lEBGWRNyJCa07v/TyOcfSDssrEtukl/sdBZOcH3jCciKhFGtIAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="824" height="345"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.b8462a8.824.png" srcset="/assets/ideal-img/6.b8462a8.824.png 824w" width="824" height="345"></noscript></div></div><p>이후 <code>resources</code> 디렉토리에 <code>index.html</code> 를 생성하고 아래와 같이 작성하도록 합니다.</p><div class="language-html codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">index.html</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-html codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token doctype punctuation" style="color:#393A34;font-style:italic">&lt;!</span><span class="token doctype doctype-tag" style="color:#999988;font-style:italic">DOCTYPE</span><span class="token doctype" style="color:#999988;font-style:italic"> </span><span class="token doctype name" style="color:#999988;font-style:italic">html</span><span class="token doctype punctuation" style="color:#393A34;font-style:italic">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">html</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">meta</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">charset</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">UTF-8</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">title</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">Document</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">title</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">body</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Hello, Tomcat!</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">body</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">html</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="실행">실행<a class="hash-link" href="#실행" title="제목으로 바로 가기">​</a></h3><p>이전에 실행한 컨테이너를 제거하고 아래의 명령어로 컨테이너를 실행합니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> run -p </span><span class="token number" style="color:#36acaa">8888</span><span class="token plain">:8080 -v C:</span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain">Users</span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain">ruddms936</span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain">Desktop</span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain">example</span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain">resources:/usr/local/tomcat/webapps/ROOT tomcat-test:practice</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>컨테이너 내부에 <code>ROOT</code>디렉토리가 없더라도 자동으로 생성됩니다.</p></div></div><p>정상적으로 동작하는 것을 확인할 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAlUlEQVQImXWMwQoCMQxE+7u2tqiI+BsqHvZLRBDxpAfZY7x1f8FC6e7SbIikeio48MhkBka55eK43mxf1q2e2tjWGFcQP9NzyUAb26h3CDecJkZEzojlFp+FXHwI4a5SSmf+KjMzVUjGKaWrijGe5CGikYiwYpQuxnhRfd8/fot/NQxDqwBAd13XeO933vtDxV46AHAfMNrPSMdS5aYAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="3808" height="2242"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.4df687a.3808.png" srcset="/assets/ideal-img/7.4df687a.3808.png 3808w" width="3808" height="2242"></noscript></div></div>]]></content>
        <category label="docker" term="docker"/>
        <category label="tomcat" term="tomcat"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[CRA를 직접 만들기 위해 필요한 의존성은 어떤 것이 있을까?]]></title>
        <id>/2020/10/24/what-are-the-standard-dependencies-for-making-cra</id>
        <link href="https://parkgang.github.io/blog/2020/10/24/what-are-the-standard-dependencies-for-making-cra"/>
        <updated>2020-10-24T03:00:00.000Z</updated>
        <summary type="html"><![CDATA[React 환경을 쉽게 구축할 수 있도록 보일러 플레이트인 create react app 를 사용하면 쉽게 환경을 찍어낼 수 있을지는 몰라도 원초적인 메커니즘을 이해하지 못하면 트러블 슈팅의 어려움, 불필요한 라이브러리 사용으로 오버헤드 유발 등 다른 문제를 유발할 수 있다.]]></summary>
        <content type="html"><![CDATA[<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAIAAAB1kpiRAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAvUlEQVQImWOQl1eSklaSllFSUlZRkJdXUFBQVlZRUgQBJSUlBmNDZT8PVXcnNUUFGX1DUx1dfXlZWVVNbVVtHSVFRYbCdK27x0yPb9R28XBtWnClfuYRXWNzw7A426JGNX1DhuJMrRuHLPav1PYLDqlb/LR+wU1TO1eThGzHun4tCxsGV0eV6gKNvBR1NVVFW7cQS3svBXk5dQMjTTNrJSVlBlk5JVFxRXFJkEPkZKXl5GSUlJVBDlOQBzoNAB3WLIGEYBT6AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1280" height="720"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/thumbnail.f3e548c.1280.png" srcset="/assets/ideal-img/thumbnail.f3e548c.1280.png 1280w" width="1280" height="720"></noscript></div></div><p>React 환경을 쉽게 구축할 수 있도록 <a href="https://brunch.co.kr/@kooslab/144" target="_blank" rel="noopener noreferrer">보일러 플레이트</a>인 <code>create react app</code> 를 사용하면 쉽게 환경을 찍어낼 수 있을지는 몰라도 원초적인 메커니즘을 이해하지 못하면 트러블 슈팅의 어려움, 불필요한 라이브러리 사용으로 오버헤드 유발 등 다른 문제를 유발할 수 있다.</p><blockquote><p>물론 CRA에는 포편적으로 필요한 기능이 잘 녹아져 있다.</p></blockquote><p><code>CRA</code> 는 내부에서 어떤 것을 사용하여 구현되어 있는 것일까? 궁금하니 직접 React 환경을 만들게 될 때 사용되는 라이브러리에 대해 알아보도록 하자</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>아래의 글을 참고하여 작성되었습니다.</p><ol><li><a href="https://medium.com/wasd/%EC%9B%B9%ED%8C%A9-webpack-%EA%B3%BC-%EB%B0%94%EB%B2%A8-babel-%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-react-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0-fb87d0027766" target="_blank" rel="noopener noreferrer">Webpack과 Babel을 이용한 React 개발 환경 구성하기</a></li><li><a href="https://velog.io/@jeff0720/React-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD%EC%9D%84-%EA%B5%AC%EC%B6%95%ED%95%98%EB%A9%B4%EC%84%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-Webpack-%EA%B8%B0%EC%B4%88#%EC%9B%B9%ED%8C%A9%EC%97%90%EC%84%9C-css-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0" target="_blank" rel="noopener noreferrer">React 개발 환경을 구축하면서 배우는 웹팩(Webpack) 기초</a></li></ol></div></div><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>CRA를 안 쓰고 직접 구현하는 만큼 필요한 의존성을 선택하여 추가할 수 있습니다.<br>
<!-- -->아래에 서술된 리스트는 그래도 개발을 위해서 필수적으로 사용되는 standard 한 친구들에 대해서 나열해 놓았습니다.</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="라이브러리-심플-설명">라이브러리 심플 설명<a class="hash-link" href="#라이브러리-심플-설명" title="제목으로 바로 가기">​</a></h2><table><thead><tr><th align="center">라이브러리</th><th align="left">설명</th></tr></thead><tbody><tr><td align="center">webpack</td><td align="left"></td></tr><tr><td align="center">webpack-cli</td><td align="left">1. cli으로 webpack을 사용할 수 있도록 도와줌 <br> 2. webpack4 부터 필요함</td></tr><tr><td align="center">webpack-dev-server</td><td align="left">server를 열어줌</td></tr><tr><td align="center">@babel/core</td><td align="left"></td></tr><tr><td align="center">@babel/preset-env</td><td align="left">함께 사용되어야 하는 Babel 플러그인을 모아 둔 것</td></tr><tr><td align="center">@babel/preset-react</td><td align="left">Babel react 프리셋</td></tr><tr><td align="center">babel-loader</td><td align="left"></td></tr><tr><td align="center">react-hot-loader</td><td align="left">hot으로 변경된 부분만 새로고침 없이 업데이트를 할 수 있지만 react의 state가 죽어버리므로 이 문제를 해결하는 라이브러리 이다.</td></tr><tr><td align="center">react</td><td align="left">react component를 사용할 수 있게 도와줌</td></tr><tr><td align="center">react-dom</td><td align="left">react를 렌더링 시켜줌</td></tr><tr><td align="center">html-loader</td><td align="left">1. 웹팩이 html 파일을 읽어서 html 파일을 빌드할 수 있게 도와줌 <br> 2. 웹팩에서 html으로 loader해서 빌드하면 장점은 html에 자동으로 빌드된 js가 추가되는점 또한 최적화 인것같다</td></tr><tr><td align="center">html-webpack-plugin</td><td align="left"></td></tr><tr><td align="center">css-loader</td><td align="left">웹팩이 css 읽도록 도와줌</td></tr><tr><td align="center">mini-css-extract-plugin</td><td align="left">웹팩의 빌드 결과물인 css를 html에 삽입할 때 사용됨(css를 파일로 추출)</td></tr><tr><td align="center">clean-webpack-plugin</td><td align="left">1. 기본적으로이 플러그인은 성공적으로 다시 빌드 한 후 webpack의 output.path 디렉토리에있는 모든 파일과 사용하지 않는 모든 웹팩 자산을 제거합니다. <br> 2. <code>{}</code> 으로 묶어서 사용</td></tr></tbody></table><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="라이브러리-딥-설명">라이브러리 딥 설명<a class="hash-link" href="#라이브러리-딥-설명" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="webpack">webpack<a class="hash-link" href="#webpack" title="제목으로 바로 가기">​</a></h3><p>webpack 4 이상으로 버전이 올라가면서 기본적인 환경설정이 추가되었다.</p><p>대부분이 webpack 가이드가 v4 이하로 설명되면서 혼란스러운 부분이 몇가지 있다.</p><p>가장 중요한 부분은 webpack 실행 전 mode 옵션을 필수로 추가해야만 한다.</p><p>때문에 <code>webpack</code> 으로 trans된 파일을 실행 시 <code>process.env.NODE_ENV</code> 환경변수 값이 <code>undefined</code> 에러가 발생한다.</p><p>v4부터는 실행 시 아래와 같이 mode를 지정해야 한다.</p><table><thead><tr><th>모드</th><th>명령어</th></tr></thead><tbody><tr><td>개발</td><td><code>webpack --mode development</code></td></tr><tr><td>제품</td><td><code>webpack --mode production</code></td></tr></tbody></table><div class="admonition admonition-tip alert alert--success"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</h5></div><div class="admonition-content"><ol><li>config에 mode를 넣어줘도 된다.</li><li>config 지정 시 <code>.js</code> 확장자를 지정해야 함<blockquote><p><code>webpack --config ./config/webpack.config.prod.js</code></p></blockquote></li><li>mode에서 none는 안 되는 것으로 확인된다.</li></ol></div></div><p>이외에도 기본적인 config가 내장되어 있는 등 변경된 부분이 많다.</p><p>위의 문제는 기존 레퍼런스 중에서 가장 많이 발생할 수 있는 문제라 서술되었다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>참조</h5></div><div class="admonition-content"><ol><li><a href="https://medium.com/@yoohl/%EC%9B%B9%ED%8C%A94-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0-e4130fc7cd80" target="_blank" rel="noopener noreferrer">webpack4의 변경 점 설명</a></li><li><a href="https://webpack.js.org/" target="_blank" rel="noopener noreferrer">webpack 공식 Docs</a></li></ol></div></div><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>실패 시 사진</h5></div><div class="admonition-content"><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAUUlEQVQImWXGQQqAIBBGYa+gZUUKM5kjZFTrFt3/WH+UbqLF43vq4gknMbbWYrcdsmmQtUHWj+WPfoBambEQQZyDuLH6LXkPFWJEEMH8lqr/brJEKVYFv5kOAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1920" height="518"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.b88311e.1920.png" srcset="/assets/ideal-img/1.b88311e.1920.png 1920w" width="1920" height="518"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAQ0lEQVQImR3HwRGAIAxFQXrRHwgkCtJ/bc8ZDnvYMtcmIon3oz8Td0cyzAxJtNZYa1OaB5lJ747VikxI93HrOh8j+AGwRB2TFnSMAAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1919" height="520"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.7bd7e9d.1919.png" srcset="/assets/ideal-img/2.7bd7e9d.1919.png 1919w" width="1919" height="520"></noscript></div></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="webpack-cli">webpack-cli<a class="hash-link" href="#webpack-cli" title="제목으로 바로 가기">​</a></h3><p>버전업이 되면서 더 이상 <code>webpack-dev-server</code> 가 실행되지 않는다. <code>webpack serve</code> 으로 호출해야한다.</p><p>하지만 위와 같이 호출해도 똑같이 환경변수에 대한 문제가 발생한다.</p><p>config에서 mode를 설정하지 않으면 <code>webpack serve --mode development --env development</code> 으로 호출한다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>참조</h5></div><div class="admonition-content"><ol><li><a href="https://stackoverflow.com/questions/59611597/error-cannot-find-module-webpack-cli-bin-config-yargs" target="_blank" rel="noopener noreferrer">stackoverflow: webpack과 버전 출동 문제</a></li><li><a href="https://github.com/webpack/webpack-dev-server/issues/2759" target="_blank" rel="noopener noreferrer">GitHub: 해당 이슈 내용</a></li><li><a href="https://romeoh.tistory.com/entry/Webpack-dev-server-%EA%B5%AC%EB%8F%99%ED%95%98%EA%B8%B0" target="_blank" rel="noopener noreferrer">webpack-cli가 아닌 경로 지정으로 실행방법</a></li><li><a href="https://sujinlee.me/webpack-react-tutorial/" target="_blank" rel="noopener noreferrer">webpack에서 사용되는 모듈의 자세한 설명</a></li><li><a href="https://ibrahimovic.tistory.com/51" target="_blank" rel="noopener noreferrer">webpack mode 설정방법</a></li></ol></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="webpack-dev-server">webpack-dev-server<a class="hash-link" href="#webpack-dev-server" title="제목으로 바로 가기">​</a></h3><p>React는 babel으로 trans된 파일이 webpack을 통해 합쳐진 상태로 렌더링 되어야 합니다.</p><p>VSCode의 익스텐션인 Live Server와 같이 선행 작업으로 Webpack의 컴파일러를 거치지 않은 HTML을 출력하게 하면 수정된 내용이 반영되지 않습니다.</p><p>그렇기 때문에 파일이 변경되면 <code>webpack build</code> → <code>server</code> 의 과정을 거쳐야하는데 <code>webpack-dev-server</code> 를 사용하면 편리하게 구현할 수 있습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="react-hot-loader">react-hot-loader<a class="hash-link" href="#react-hot-loader" title="제목으로 바로 가기">​</a></h3><p>적용방법은 아래와 같습니다.</p><ol><li><code>yarn add --dev react-hot-loader</code> 설치</li><li>devServer에 <code>hot: true</code> 설정<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAARElEQVQImWNwM/P8H2Tt+9/Z2PW/pqHzfx1Dt/86hi7/9Yxc/msbOP7XN3X4r2Pk9J/BxNj9v5OFx39rU5f/Wga4FQIAR68hNZvoREEAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="408" height="98"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.81b40e1.408.png" srcset="/assets/ideal-img/3.81b40e1.408.png 408w" width="408" height="98"></noscript></div></div></li><li>최상위 컴포넌트에
<code>import { hot } from "react-hot-loader";</code> 와
<code>export default hot(module)(App);</code> 추가<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAyElEQVQYlWWPS26DMBRFWU0KwcYGbH7B1E6AhKpSK3XQ/a/kVFhpGXRwR++8+0ku7oOh2xjGjaEPtMZTqIlUODI5/Sm5TV88wjdNu9F1HtP4ePgHKvugGT8R+sZL7qJScQAHaDZydeWUX0ilIxM7OEbHqPjoSApzp202tJ6RKpCXM6JcEeWMrAPKBoT2JJl8xfXv2PYNVS8ouyDrFW1WitJzLibOe7TQAaF8tD/tUc9OO/A7KkZXZkZVC0pf8f0dW+19x2fXY/kPJ06BkB+89JEAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="559" height="442"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.d612646.559.png" srcset="/assets/ideal-img/4.d612646.559.png 559w" width="559" height="442"></noscript></div></div></li></ol><blockquote><p>신기하게 babel에 plugins을 지정하지 않아도 동작한다.</p></blockquote><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>이전의 export 구분</h5></div><div class="admonition-content"><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAMklEQVQImWPwDcn87xOc/t/PN+l/iHf0f9eg1P/2UQX/HaOL/nvFlf8PTSv9H5kQ9x8Ak7gUd41ddxgAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="461" height="19"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.36f1d55.461.png" srcset="/assets/ideal-img/5.36f1d55.461.png 461w" width="461" height="19"></noscript></div></div></div></div><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>참조</h5></div><div class="admonition-content"><ol><li><a href="https://react-etc.vlpt.us/05.hot-loader.html" target="_blank" rel="noopener noreferrer">react-hot-loader 적용하기</a></li><li><a href="https://smoothlog.com/react-hot-loader-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0" target="_blank" rel="noopener noreferrer">React-hot-loader 사용하기</a></li></ol></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>CRA 대비 직접 구현해 보면 알 수 있지만 React 환경에 거의 필수인 Babel, Webpack에 대해 알게 된다.</p><p>특히 <code>Webpack</code> 의 기능은 무궁무진하다. 해당 메커니즘을 알게 되면 React 환경 이외에도 JS 프로젝트에서 아주 많은 곳에서 사용되므로 CRA 대신 바닥부터 만들어보면서 생긴 경험치는 나중에 많은 도움이 될 것이다.</p><p>원래는 실제로 구현한 프로젝트 코드와 구현 방법까지 설명하려고 했지만 구현 당시 프로젝트가 독립된 Repo가 아니라서 해당 글을 고려하지 않은 바탕으로 여러 번 리펙토링 되면서 해당 글만을 위한 코드로 뽑아오기가 애매해졌다.</p><p>그래서 아쉬운 마음에 구현하면서 사용된 의존성 리스트와 트러블 슈팅 내역에 대해 공유하게 되었디.</p><blockquote><p>처음 구현할 땐 의존성 끼리 오류가 너무 많아서 고생했습니다ㅠㅠ</p></blockquote><p>읽어주셔서 감사합니다 :)</p>]]></content>
        <category label="react" term="react"/>
        <category label="babel" term="babel"/>
        <category label="webpack" term="webpack"/>
        <category label="CRA" term="CRA"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Git을 이용해 통일된 개발 환경은 가능할까?]]></title>
        <id>/2020/10/17/unified-dev-env-possible-using-git</id>
        <link href="https://parkgang.github.io/blog/2020/10/17/unified-dev-env-possible-using-git"/>
        <updated>2020-10-17T05:00:00.000Z</updated>
        <summary type="html"><![CDATA[문득 Git으로 모든 개발 환경을 통합할 수 있을까라는 생각이 들어서 내 생각을 정리해 보았다.]]></summary>
        <content type="html"><![CDATA[<p>문득 Git으로 모든 개발 환경을 통합할 수 있을까라는 생각이 들어서 내 생각을 정리해 보았다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="모두-다른-ide-통합할-수는-없을까">모두 다른 IDE 통합할 수는 없을까?<a class="hash-link" href="#모두-다른-ide-통합할-수는-없을까" title="제목으로 바로 가기">​</a></h2><p>하나의 SW 제품을 개발하기 위해서 사용되는 Tool은 가지각색이다.</p><p>특히 IDE의 경우 취향을 많이 타게되는데 SW를 개발하는 시간 중 가장 많은 시간을 보내는 Tool이기도 하면서 익숙해진 단축키와 UI/UX에 종속된 개발자들은 쉽게 다른 Tool으로 눈을 돌리기가 쉽지 않다.</p><p>이로 인해 발생하는 문제로 프로젝트에서 나와 다른 IDE를 만나는 경우 일 것이다.</p><p>갑자기 호기심으로 이러한 생각이 들었다.</p><blockquote><p>git은 형상관리 Tool인데 소스코드만 저장하고 사용자마다 다른 IDE에서 불러오면 되는거 아닌가?</p></blockquote><p>아니나 다를까 현업에서는 이를 위해 IDE와 같은 설정파일은 commit하지 않는다고 한다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="의문점">의문점<a class="hash-link" href="#의문점" title="제목으로 바로 가기">​</a></h2><p>하지만 IDE에 종속된 기능의 경우는 어떻게 해결할까?</p><ol><li>여러게의 프로젝트가 의존되어 있어 build 순서를 지정해야 하는 경우</li><li>일부파일이 프로젝트 속성으로 빌드 제외된 경우</li><li>등등..</li></ol><p>해당 속성들은 나의 IDE 환경설정에 종속되어 작동하므로 다른 설정 혹은 IDE에서 사용시 일일이 Setting 해줘야한다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="해결법">해결법<a class="hash-link" href="#해결법" title="제목으로 바로 가기">​</a></h2><p>물론 이러한 문제를 방지하기 위해 Maven, Gradle등 과 같은 빌드 자동화 시스템이 존재한다.</p><p>하지만 정말 IDE에 종속된 설정같은건 어떻게 해결할까? 종속되지 않도록 노력할까? 아니면 그런 기능은 존재하지 않을까</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="결론">결론<a class="hash-link" href="#결론" title="제목으로 바로 가기">​</a></h2><p>아직 경험이 많이 부족하여 완벽하게 궁금증을 해소하지는 못했다.</p><p>하지만 항상 이런 호기심을 가지고 접근을 하다 보면 언젠가 내가 이상적으로 생각하는 또한 팀원에게 도움이 되는 개발 환경을 만들 수 있지 아늘까?</p><p>항상 되는 것보다는 왜 되는지가 더 중요한 나에게는 하나의 IDE를 배울 때도 엄청난 시간이 소모됬다.</p><p>파일을 하나씩 까보며 삭제하면 어떻게 될 까? 이거는 꼭 필요한 파일일까? 하면서 하루를 보낸 적도 많다.</p><blockquote><p>그만하고 언어 공부 해야되는데...😨</p></blockquote><p>그래도 돌이켜 보면 그러면서 배운 것들이 나중에 많은 도움이 되어서 후회는 없다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="reference">Reference<a class="hash-link" href="#reference" title="제목으로 바로 가기">​</a></h2><ol><li><a href="https://okky.kr/article/523113" target="_blank" rel="noopener noreferrer">각 IDE(이클립스, 넷빈즈, 인텔리j)으로 GitHub, gitlab에 등록된 프로젝트를 다른 IDE로 바로 적용 할 수 없나요?</a></li><li><a href="https://okky.kr/article/278154" target="_blank" rel="noopener noreferrer">하나의 프로젝트를 IntelliJ와 Eclipse로 같이 협업 가능 할까요?</a></li></ol>]]></content>
        <category label="git" term="git"/>
        <category label="essay" term="essay"/>
        <category label="DevOps" term="DevOps"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[zsh 설치 및 플러그인 설치 (with oh my zsh)]]></title>
        <id>/2020/10/17/install-zsh-and-plugins-with-oh-my-zsh</id>
        <link href="https://parkgang.github.io/blog/2020/10/17/install-zsh-and-plugins-with-oh-my-zsh"/>
        <updated>2020-10-17T04:00:00.000Z</updated>
        <summary type="html"><![CDATA[zsh 에 oh my zsh 를 적용하고 Powerlevel10k 를 이용해 Terminal 을 이쁘게 꾸며보겠습니다. 또한, 플러그인을 설치하여 생산성까지 강화해 보도록 하겠습니다.]]></summary>
        <content type="html"><![CDATA[<p><code>zsh</code> 에 <code>oh my zsh</code> 를 적용하고 <code>Powerlevel10k</code> 를 이용해 <code>Terminal</code> 을 이쁘게 꾸며보겠습니다. 또한, 플러그인을 설치하여 생산성까지 강화해 보도록 하겠습니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="각각의-어떤-것을-의미하나요">각각의 어떤 것을 의미하나요?<a class="hash-link" href="#각각의-어떤-것을-의미하나요" title="제목으로 바로 가기">​</a></h2><p><code>Terminal</code> 좀 이쁘게 바꿔보려고 하는데 뭐 이리 많은 것을 설치해야 되는지 헷갈립니다. 우선 정리를 하고 가볼까요?</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="zsh">zsh<a class="hash-link" href="#zsh" title="제목으로 바로 가기">​</a></h3><p>bash와 같은 shell 프로그램입니다. bash에 비해 강력한 기능과 여러 가지 플러그인을 제공하고 있습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="oh-my-zsh">oh my zsh<a class="hash-link" href="#oh-my-zsh" title="제목으로 바로 가기">​</a></h3><p>zsh 설정을 관리하기 위한 프레임워크입니다. 현재를 기준으로 200개가 넘는 플러그인과 140개 이상의 테마를 제공합니다.</p><p><code>Powerlevel10k</code> 와 함께 사용할 경우 예쁘고 편리한 shell 사용 환경을 쉽게 갖출 수 있습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="powerlevel10k">Powerlevel10k<a class="hash-link" href="#powerlevel10k" title="제목으로 바로 가기">​</a></h3><p>zsh의 이쁜 테마를 제공합니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="zsh-설치">zsh 설치<a class="hash-link" href="#zsh-설치" title="제목으로 바로 가기">​</a></h2><p>우선 Terminal 꾸미기 및 기능을 추가하려면 근간이 되는 shell이 먼저 필요하겠죠? 아래의 명령어로 <code>zsh</code> 을 설치하도록 합니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt-get</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">zsh</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAvklEQVQImS3H0U7CMACF4T4WahBCNlhagmEXndm6Ad1YOwhbFFnEqHc+92+UXXz5zxGn4w/V7otUn8n0GR23JHE3uG29bhF61bEKHXJiWTwUhPeGYJTd3A0dZYgm/8abTyrdkz+1pPJAsnA8R45UepJ5/f/FZfPBq7nSrDtq5fHLhnrZ4Ib+2SuPOEaWLrJcVcmbLHHzHb209KrkXVkuquJFWYR6tOwnBjMtiMYF8bjAzzZspzmHoMAEFT7c8gtRtnuaGeoRQwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1008"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.f9425c4.1680.png" srcset="/assets/ideal-img/1.f9425c4.1680.png 1680w" width="1680" height="1008"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="oh-my-zsh-설치">oh-my-zsh 설치<a class="hash-link" href="#oh-my-zsh-설치" title="제목으로 바로 가기">​</a></h2><p>아래의 명령어로 <code>oh-my-zsh</code> 를 설치하도록 합니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">sh</span><span class="token plain"> -c </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable function" style="color:#d73a49">wget</span><span class="token string variable" style="color:#36acaa"> -O- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh</span><span class="token string variable" style="color:#36acaa">)</span><span class="token string" style="color:#e3116c">"</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAwElEQVQImUWH0UrDMAAA81sKgpO0bqZQrTNtpU1j17RNlehEu6kg+OB3nygDH467E+Hhm9F9cWc+aM07tn6jKl641c+U6y359SM6C4g0nkijEbXoWJ5Y4uOa6I/q4Bp5VCFcuccVM81VoFx5dNyTn/cUy4FyNZAfXoSbwH024VPPkIz4xNMnv/1vp0bEU+aY145P3bO77PAXLbtkw6w27FXLq+rYqhaRRo5BGkzUoBYN+qzBS4s9rZmkoZIdY2T5AZxBfB0U4ewAAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1006"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.b0d61ab.1680.png" srcset="/assets/ideal-img/2.b0d61ab.1680.png 1680w" width="1680" height="1006"></noscript></div></div><div class="admonition admonition-tip alert alert--success"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</h5></div><div class="admonition-content"><p><code>oh-my-zsh</code> 설치 시 기본 셀을 <code>zsh</code> 으로 설정할 수 있지만 <code>chsh -s (which zsh)</code> 을 통해서도 변경이 가능합니다.</p><p><code>zsh</code> 이 기본이면 안 해도 됩니다.</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="power-level-10kp10k-theme-설치">power level 10k(p10k) theme 설치<a class="hash-link" href="#power-level-10kp10k-theme-설치" title="제목으로 바로 가기">​</a></h2><p>아래의 명령어로 <code>Powerlevel10k</code> 를 설치하도록 합니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">git</span><span class="token plain"> clone --depth</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> https://github.com/romkatv/powerlevel10k.git </span><span class="token variable" style="color:#36acaa">${ZSH_CUSTOM</span><span class="token variable operator" style="color:#393A34">:-</span><span class="token variable" style="color:#36acaa">~</span><span class="token variable operator" style="color:#393A34">/</span><span class="token variable" style="color:#36acaa">.oh-my-zsh</span><span class="token variable operator" style="color:#393A34">/</span><span class="token variable" style="color:#36acaa">custom}</span><span class="token plain">/themes/powerlevel10k</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAs0lEQVQImVXI3U7CQBRF4XksgvITrG3TKRdAoFLLUGYqMwVShWgDCbcmvvUyNETh4ss6+4hq98Ou/MbkZ1RaM5/uSaeHRjJ5JxlXzEYVInhc4j8o/PYCr/X656mV3m2R+GsawU0Dy0toSSN3/VmEjR02LlnHZdNy+H9fevEmHWIbGj5CzUkaPiOD9VfUkaaWhqPUfMmCvdQI2dUUvYysrwg7inFH4QZL8v6CjafIvAL3nPMLql96XkxJgZsAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1006"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.069436c.1680.png" srcset="/assets/ideal-img/3.069436c.1680.png 1680w" width="1680" height="1006"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="theme-적용">theme 적용<a class="hash-link" href="#theme-적용" title="제목으로 바로 가기">​</a></h3><p><code>code ~/.zshrc</code> 또는 <code>nano ~/.zshrc</code> 으로 접속해서 아래와 같이 수정하도록 합니다.</p><div class="language-diff codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-diff codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49"> ZSH_THEME="robbyrussell"</span><br></span><span class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa"> ZSH_THEME="powerlevel10k/powerlevel10k"</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="admonition admonition-tip alert alert--success"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>VSCode를 사용 중이면 아래와 같이 수정해보세요.</h5></div><div class="admonition-content"><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAgUlEQVQImQXBTQ7BQACA0blTK6qJBRqSLpT+6NSiK8JONNHEgiPKYCqpWDOaOsPnPVFpzetZ86hq1OXGVWk+74am+fE1Lca0KHVH+OMVQVgQ52fi/ES0KMmSkvl0RzTbs5RH/MkW4XYS+sM1XnhgFBQMvA2uLelaKY4l6dkZjpXyB73gSyC4mTSQAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="264" height="89"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.83d086c.264.png" srcset="/assets/ideal-img/4.83d086c.264.png 264w" width="264" height="89"></noscript></div></div><p><code>VSCode</code> 가 없다면 <code>텍스트 편집기</code> 로 열어서 수정만 하면 됩니다.</p><blockquote><p>아니면 어썸하게 <code>vim</code> 으로!</p></blockquote></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="font-설치-및-적용">font 설치 및 적용<a class="hash-link" href="#font-설치-및-적용" title="제목으로 바로 가기">​</a></h3><p>font 설치의 경우 Terminal Client Program 마다 조금 씩 다른데 <code>Mac</code> 에서 많이 사용되는 <code>iTerm2</code> 과 Windows 에서 많이 사용되는 <code>Windows Terminal</code> 애 대해서 가이드 하겠습니다.</p><h4 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="iterm2">iTerm2<a class="hash-link" href="#iterm2" title="제목으로 바로 가기">​</a></h4><p><code>power level 10k</code> 셋팅 시 폰트 설치 여부에 따라 시작화면에 설치방법을 가이드합니다.</p><p>Meslo Nerd Font 설치 여부를 묻는 메시지가 표시되면 입력 p10k configure하고 대답 Yes합니다.</p><h4 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="windows-terminal">Windows Terminal<a class="hash-link" href="#windows-terminal" title="제목으로 바로 가기">​</a></h4><p>Windows Terminal에서 폰트가 깨지기 때문에 필요한 <a href="https://github.com/romkatv/powerlevel10k/#user-content-fonts" target="_blank" rel="noopener noreferrer">폰트를 다운받습니다.</a></p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAvUlEQVQYlV2N20oEMRBE5///SpHV0ZUV9VnERzNr7ulkZpIjCd6woSjoPtU1AbTWsM7jQiKkQpRMSgkRQSRTa2X6Bo0xqOXMctZoYxFJlFIoZf0PWt7VgtYG7z0hRNzwwL7vv6BzgeXDYn0cn9a1sG3bUL//gD3pnCNnIY/KMiq7Blhr6yjGC0rLWHbV1ti/wO7jY5+YMj5m9lFVR/jvTC+vb3jnUErx8PTM5fXM1XzHxeGGw+2R+f7E8fTIJ6s/NLLI1c8uAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1165" height="907"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.c04f585.1165.png" srcset="/assets/ideal-img/5.c04f585.1165.png 1165w" width="1165" height="907"></noscript></div></div><p>Windows Terminal에서 <code>Ctrl</code> + <code>,</code> 으로 들어가 설치한 폰트 적용해줍니다.</p><div class="codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-text codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">"fontFace": "MesloLGS NF"</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAi0lEQVQImV3KSQ6DMBBEUe4RD9h0225jAtz/cD8ik5QsnmpRf9rPg20f3PfBOjq2GmaGqiIiiCh97Uy1VcqqtJ6Rmgizxzn35b1HdLnChg5jaZlcEjFFQgyE8GvSUqjbSamVFANpjq/T/4UiC7kItWbm2eODw138x+2502YHQ092vWPR0NAoby31rweiF1SZ+3PolAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1012"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.8033a94.1680.png" srcset="/assets/ideal-img/6.8033a94.1680.png 1680w" width="1680" height="1012"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="theme-setting">theme setting<a class="hash-link" href="#theme-setting" title="제목으로 바로 가기">​</a></h3><p><code>p10k configure</code> 을 입력하면 테마를 재설정 할 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAtUlEQVQImT3IXU/CMBiG4f6sSUAJjK3ZiwdAIpNtHXSTdpIgJEhU4m+/TXXx4Mrzoay5YetvbH1jZ74o8wv56sR6eey98bQ4opJBRRwVpEPDbFAxjTa/O77rhR4VqEIOPGvPOtn/SQNHrh2brP9Th3Li2c+DDjfv6B67/x4yeBGPOuiGk7Z8SsMla3DJjmtmuUrDh1jepeUsFiX3lvahpBwb9MiwHBn8pGY7rniNDWXc4mdbfgBz83opkY05RAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1007"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.2d938aa.1680.png" srcset="/assets/ideal-img/7.2d938aa.1680.png 1680w" width="1680" height="1007"></noscript></div></div><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>저의 경우 아래와 같이 설정했습니다.</p><div class="codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-text codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">Prompt Style        : (3)  Rainbow</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Character Set       : (2)  ASCII</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Show current time?  : (3)  12-hour format</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Prompt Separators   : (1)  Angled</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Prompt Heads        : (1)  Sharp</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Prompt Tails        : (1)  Flat</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Prompt Height       : (1)  One line</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Prompt Spacing      : (2)  Sparse</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Icons           : (2)  Many icons</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Prompt Flow     : (1)  Concise</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Enable Transient Prompt?: (n)  No</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-text codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">Instant Prompt Mode     : (1)  Verbose (recommended)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Apply changes to ~/.zshrc?  : (y)  Yes (recommended)</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="zsh--oh-my-zsh--powerlevel10k-적용-완료"><code>zsh</code> + <code>oh my zsh</code> + <code>Powerlevel10k</code> 적용 완료<a class="hash-link" href="#zsh--oh-my-zsh--powerlevel10k-적용-완료" title="제목으로 바로 가기">​</a></h2><p>최종적으로 모두 적용되면 아래와 같은 모습을 살펴볼 수 있습니다.</p><p>이외로 Windows Terminer에 <code>WSL</code> 을 기본 프로필로 사용하면 Windows에서 더욱 <code>Linux</code> 를 사용하는 느낌을 낼 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA2klEQVQImQXBXU+CUACAYX5XmwsTTT6cm2zx4eGAgHBQAzaVWK1Wyy66aat10UXr73TbP3p7Hu3185v+4Y087wivGkKvxXNrpNijdnf4bksierS6fyZYHnFGEksXWMMIeySZmymLWc7lWcTMKNBOH7+8fP1R795Z2iX+VBHaiiy4JrAKCv/A6fEHrRX3NNETrbxl626onIrCqVhbisKuKOdbau+AVo1j6nHEzTRmb6aUkxWdldI5Gb2dcrQyGjNBc84z1nqEGMaYA8liIFEXMYkesTEkwshRkxX/rmCAhEbHEQUAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1010"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.53445d3.1680.png" srcset="/assets/ideal-img/8.53445d3.1680.png 1680w" width="1680" height="1010"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="플러그인-활성화-하기">플러그인 활성화 하기<a class="hash-link" href="#플러그인-활성화-하기" title="제목으로 바로 가기">​</a></h2><p>이제 <code>zsh</code> 를 설치했으니 플러그인을 설치하여 강력한 <code>zsh</code> 의 기능을 사용해봅시다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p><a href="https://blog.jeuke.com/77" target="_blank" rel="noopener noreferrer">[Linux] zsh 플러그인 설치와 추천 플러그인</a> 를 참고하였습니다.</p></div></div><p>플러그인 활성화는 <code>.zshrc</code> 에서 할 수 있습니다. 저는 아래의 플러그인을 기본적으로 활성화시켜 놓았습니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token assign-left variable" style="color:#36acaa">plugins</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">git</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">yarn</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">node</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  brew</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">docker</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="하이라이팅-자동완성-플러그인-설치">하이라이팅, 자동완성 플러그인 설치<a class="hash-link" href="#하이라이팅-자동완성-플러그인-설치" title="제목으로 바로 가기">​</a></h2><p>기본 플러그인 외에 명령어 하이라이팅 플러그인 <code>zsh-syntax-highlighting</code> 과 자동완성 플러그인 <code>zsh-autosuggestions</code> 설치합니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># zsh-syntax-highlighting</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">git</span><span class="token plain"> clone https://github.com/zsh-users/zsh-syntax-highlighting.git </span><span class="token variable" style="color:#36acaa">${ZSH_CUSTOM</span><span class="token variable operator" style="color:#393A34">:-</span><span class="token variable" style="color:#36acaa">~</span><span class="token variable operator" style="color:#393A34">/</span><span class="token variable" style="color:#36acaa">.oh-my-zsh</span><span class="token variable operator" style="color:#393A34">/</span><span class="token variable" style="color:#36acaa">custom}</span><span class="token plain">/plugins/zsh-syntax-highlighting</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># zsh-autosuggestions</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">git</span><span class="token plain"> clone https://github.com/zsh-users/zsh-autosuggestions </span><span class="token variable" style="color:#36acaa">${ZSH_CUSTOM</span><span class="token variable operator" style="color:#393A34">:-</span><span class="token variable" style="color:#36acaa">~</span><span class="token variable operator" style="color:#393A34">/</span><span class="token variable" style="color:#36acaa">.oh-my-zsh</span><span class="token variable operator" style="color:#393A34">/</span><span class="token variable" style="color:#36acaa">custom}</span><span class="token plain">/plugins/zsh-autosuggestions</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAtklEQVQImWXGwW6CMACA4b7VEhNnJLghZRrZQVBb1xalBBLHjBD3BNt1h73uv4TTjIcv/y+6/pf24wejP9GbDpVf2K7Pd0SW9uSvV9J5wyIsiR4Ns5G6ETzkiLr4prJfvK1atklNFnmy53LoJvZD92mLKBc1x5eag/QDn1Qc/zkkFX7ZIJrIcooM18TRSYd/MlxiO3wvLb10vMcWISeOYrJDTzXxWLMaK6pgj5kqmlChwgI3M/wBZLp8y3vVo0sAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1673" height="1011"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9.88eeb23.1673.png" srcset="/assets/ideal-img/9.88eeb23.1673.png 1673w" width="1673" height="1011"></noscript></div></div><p><code>code ~/.zshrc</code> 파일을 열고 plugins 항목에 플러그인을 추가합니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token assign-left variable" style="color:#36acaa">plugins</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">git</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  zsh-syntax-highlighting</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  zsh-autosuggestions</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAgklEQVQImW3NWQ4CMQwD0N6DZunQpkkrFsH9z2Y0HYQE4uPJP5adrvcbYjq6dzRrMLOl1vrhw5H2Uo2CzRQsjJzzQkRHMkHPijRGrCVRATP/JSJIPQxbU2gRMPFhL7xzXy6lIHU3aGPIxmAlZMo/Tus+XZ4PTJuIOtA1YOxfvAy4Bl4HWFOmE3xISwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1012"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/10.a08e973.1680.png" srcset="/assets/ideal-img/10.a08e973.1680.png 1680w" width="1680" height="1012"></noscript></div></div><p>설정 파일을 수정했으면 터미널을 재시작하거나 <code>source ~/.zshrc</code> 명령어를 실행하여 설정을 다시 불러와야 합니다.</p><p>이제 명령어를 입력할 때 존재하지 않는 명령어는 빨간색으로 뜨고 한번 입력했던 명령어를 흐릿하게 표현해 주는 걸 확인을 통해 적용된 것을 확인할 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAJUlEQVQImWNYsPzw/8mzdvwvKWqH41IoXVzQ+r+mqu9/d8+C/wARwhmrY5r9KwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="78"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/11.de64936.1680.png" srcset="/assets/ideal-img/11.de64936.1680.png 1680w" width="1680" height="78"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="lsd">lsd<a class="hash-link" href="#lsd" title="제목으로 바로 가기">​</a></h2><p><code>lsd</code> 는 <code>ls</code> 명령어시 파일 아이콘이 보여주는 이쁜 플러그인 입니다. 확장자에 따라서 파일 아이콘도 바뀌니 가독성도 개선될 것 입니다.</p><p>자세한 것은 <a href="https://github.com/Peltoche/lsd" target="_blank" rel="noopener noreferrer">Peltoche/lsd</a> 의 Repo에서 확인하실 수 있습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="macos-설치-방법">macOS 설치 방법<a class="hash-link" href="#macos-설치-방법" title="제목으로 바로 가기">​</a></h3><p>정말 간단합니다.<code>brew install lsd</code> 으로 설치하면 끝입니다. <code>lsd</code> 명령어를 통해서 동작 여부를 확인할 수 있습니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="linux-설치-방법-feat-wsl">linux 설치 방법 (feat. WSL)<a class="hash-link" href="#linux-설치-방법-feat-wsl" title="제목으로 바로 가기">​</a></h3><p><a href="https://github.com/Peltoche/lsd" target="_blank" rel="noopener noreferrer">Peltoche/lsd</a> 에서 release file을 다운받습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAeklEQVQImU3OCQ7DIAwEQP7/0UhR6cHp4GNTICm1tMJYA7L7xAgiQsoF+8PDvwJSrshlJaYMh6uYGSFElHrco1+pCJyZjUs9GPu7gZqiz1QNIgoRGVmQGJuvKCQA7MIT9n7BTHj6BLWJ/mE/3XQ2fmys49ENR/qOX3gC8c3r9gtkWaAAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1010"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/12.485e406.1680.png" srcset="/assets/ideal-img/12.485e406.1680.png 1680w" width="1680" height="1010"></noscript></div></div><p>다운받은 파일 경로 입니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAa0lEQVQImVWM2wrEMAhE8///uU/aTWlDjDFmSiwtdODghaPpxwdqrTAbcPe3PgwzdBtIxIScd2zbH2aGUkogIphzBivpPDOYGUSE3vtHdL/FRaqiKKLI+xEfW2tQ1Th6voU43WO4F2/7yRIvFMLD1a9YtKAAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1124" height="634"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/13.5c30d52.1124.png" srcset="/assets/ideal-img/13.5c30d52.1124.png 1124w" width="1124" height="634"></noscript></div></div><p><code>sudo dpkg -i lsd_0.18.0_amd64.deb</code> 명령어를 이용해 설치합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAzElEQVQImS3Hy06DQABA0fkxUqINVGagVi0aYGg7FBge2tjYpjTRGBfGjX9h+pvX+FjcnFzx/Hbi8PJFYV5J4i13N1vi2QPxbMP88h6d7Oi7d0SWDNzOj1xHPYGrUecLfCfjwtW/+k7O1LOIvvmgtZ+UeiANS/S0IVUWHf2ZqI4qPSJs2FFHHZVqqaWlUQ2V/E811D8ftohuYtj4KwZpeAoKam/FPjDsZcFBGnZyzaM0CDkylKOMzM2ZODlXTo49W7B0Ne04Jxuvsd6SbxVIfcaMT0r9AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1010"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/14.0f0ae54.1680.png" srcset="/assets/ideal-img/14.0f0ae54.1680.png 1680w" width="1680" height="1010"></noscript></div></div><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p><code>Downloads</code> 으로 이동하는 이유는 해당 디렉터리가 아까 설치한 lsd가 있기 때문입니다.</p></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="단축기-설정">단축기 설정<a class="hash-link" href="#단축기-설정" title="제목으로 바로 가기">​</a></h3><p><code>code ~/.zshrc</code> 에서 사용하기 쉽게 단축키를 맵핑해주도록 합니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token builtin class-name">alias</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">ls</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"lsd --no-symlink"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">alias</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">ll</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"lsd -l --no-symlink"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">alias</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">lt</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"lsd --tree --no-symlink"</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAeElEQVQImW3NXQoCMQwE4N7D/DRr2yRbVtb7H26ECoKuDx+ThxlSzvOJOSfGGGitr3zf7SP2QIl0bN0gyiAmEH1jYdi9ong6rCuqKUQFIv+VnIHmtlar/FNgFlg1lAjH8A22KUjoim/rU8njgc4O18SQuAjbETXxAvBaU3U8YFmYAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1010"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/15.a4b5dff.1680.png" srcset="/assets/ideal-img/15.a4b5dff.1680.png 1680w" width="1680" height="1010"></noscript></div></div><p>적용된 것을 확인할 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAyUlEQVQImSXFa0+CUACA4fO/at1AZFPRhgfHDvfbAbQQpFXG1vreR//v22Yfnj3idbigm18iNZKnJ6KgJw6H64F/JE4msmRCyM3IdtMzN1PmZoZ9F2PdBlezG4X9mLM0SsQpe6dLP3j2KtZeh7/s8cySnaXxbc3OqlCLFjElPed8JPYPJFHPUQ3UTkO9bv85DeWiRnyHL/zEHZ2v2Qd7Oqk5uwVfsmLaFny6FcMqR2TugTdZI1cpM6dFPlXoh5DkPqQ1IpRRUFspf9bqfqxxrN9GAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1009"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/16.9a59e46.1680.png" srcset="/assets/ideal-img/16.9a59e46.1680.png 1680w" width="1680" height="1009"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAu0lEQVQImUXG60rDMABA4TyWImNDarVNFFu0dTaLbdMmjeIu4i7qIwi+8pENmT8+zhHr7Q/z5TfVdEuRvVLmi6Mim/NYvNGYT8TdbUemGuKzGdFJRXRa/fdII+r7HU35hb5ZUFz0lJf+4OHKM032AnW+QrjU0ScOJwe8CoTrgFPh8O7vu9QjXhLLMmn5UJb31OLjhk3aspGWnWxZy46Vsgg5bunHGj0xJCNDPjIM50/UkxnPkUFHHUNc8ws98Hs/6A4L/wAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1010"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/17.7f45763.1680.png" srcset="/assets/ideal-img/17.7f45763.1680.png 1680w" width="1680" height="1010"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="bat">bat<a class="hash-link" href="#bat" title="제목으로 바로 가기">​</a></h2><p><code>cat</code> 명령어에 코드 하이라이팅 + more 기능이 추가된 버전입니다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="macos-설치-방법-1">macOS 설치 방법<a class="hash-link" href="#macos-설치-방법-1" title="제목으로 바로 가기">​</a></h3><p>아래의 명렁어 실행 후 <code>bat</code> 를 실행해서 잘 적용됐는지 확인하도록 합니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">brew </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> bat</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>파일 이름이랑 코드 줄까지 이쁘게 표시되는 것을 볼 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAABVElEQVQYlVWPbUvCUBTH7wsxH6fWGGop6oZud87mvPe6WuWyB41CCkopP0PamwjpTZ9B9i76nIXDnbiLoF78OH84P/6Hgyb3Dwf2YPReN9my1qKeYjKv3ra9mkE8ZZctR7d3H1fXN32k2O5z2e5DVmcgaAQymEJGpyDgn5y3HJAM+obEJn2ULAdE0/kSdLZKasRPhVA+P6OKCdEKfkHYvZiXnCFIbcfPtewgpRFIYwppjQQCpn7DHYBs9xYoq1rzAulB1Tn1M00WcEngIqahKLUdEHWyQLEKnu90XZCPBj5f/jamNMKzn1Q7kFAMLmqzbdYD9fjSlw/Pg5wRnufNQVojfoKLsrFAtf2TWaF7BtlmdyWazjqjs3Ua0xD+XMMdgrzXW6CcZj0JmEGyYYWk1M4/NltdyKntVxTZKsgb+fI0XqyO48XK5C+xQmUckUrTiFisfwMUi310NfG7JQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1828" height="1820"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/18.e6dfc0b.1828.png" srcset="/assets/ideal-img/18.e6dfc0b.1828.png 1828w" width="1828" height="1820"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="단축키-설정">단축키 설정<a class="hash-link" href="#단축키-설정" title="제목으로 바로 가기">​</a></h3><p><code>code ~/.zshrc</code> 에 cat 대신 사용하도록 설정하면 더 편리합니다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token builtin class-name">alias</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">cat</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"bat"</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p><code>Terminal</code> 꾸미기 등 시리즈로는 제 글 이외 많은 자료가 존재합니다. 그만큼 아무래도 개발자라면 <code>Terminal</code> 을 이쁘게 꾸미고 생산성을 강화하고 싶어 합니다.</p><p>문제는 <code>Terminal</code> 커스텀의 끝이 참으로 없다는 것인데 너무 꾸미는 데에만 집중하느라 본질을 놓치지 않았으면 좋겠습니다. 적당한 타협해서 커스텀 해주면 이뻐서 자주 들어가고 가독성 개선 및 생산성 향상이 될 거 같아요.</p><p>읽어주셔서 감사합니다.</p>]]></content>
        <category label="zsh" term="zsh"/>
        <category label="oh my zsh" term="oh my zsh"/>
        <category label="terminal" term="terminal"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[GitHub Token으로 IntelliJ Organizations Repos Push Failed 문제 해결하기]]></title>
        <id>/2020/10/17/solve-the-intellij-organizations-repos-push-failed-problem-with-github-token</id>
        <link href="https://parkgang.github.io/blog/2020/10/17/solve-the-intellij-organizations-repos-push-failed-problem-with-github-token"/>
        <updated>2020-10-17T03:00:00.000Z</updated>
        <summary type="html"><![CDATA[IntelliJ로 개인 repos는 문제가 없는데 Organizations repos push가 계속 failed해서 토큰을 발급받아 해결해보려고 합니다.]]></summary>
        <content type="html"><![CDATA[<p>IntelliJ로 개인 repos는 문제가 없는데 Organizations repos push가 계속 failed해서 토큰을 발급받아 해결해보려고 합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAARUlEQVQImR3KQQrAIAwAQd/TNKIYW2MC/f+rtuBhblO+SNKD5YnNh9E7VSt6KCKCyE3JOXF7GbYZY2GtoSJcJwjLA9/BDwdDH8qiIZc5AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1898" height="411"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/thumbnail.769f55d.1898.png" srcset="/assets/ideal-img/thumbnail.769f55d.1898.png 1898w" width="1898" height="411"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="github-token-생성">GitHub Token 생성<a class="hash-link" href="#github-token-생성" title="제목으로 바로 가기">​</a></h2><p>GitHub에서 <code>Settings</code> → <code>Developer settings</code> → <code>Personal access tokens</code> 으로 가서 <code>Generate new token</code> 를 클릭한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAY0lEQVQImV2MCQqAIBBFvf+Jyl3pRlEu6fxQK8IHn+EvDHN+g3EeUltwobBwAS41ViG7F8pAaQuGiUJzMmClFDTVWpHzhf2IOELCGSJSyr1rYkTjxXtnWt6aPvyPP/+oN0S4AfQkvsclqpOZAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1920" height="934"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.564e38e.1920.png" srcset="/assets/ideal-img/1.564e38e.1920.png 1920w" width="1920" height="934"></noscript></div></div><p>필요한 권한은 <code>repo</code> , <code>read.org</code> , <code>gist</code> 이다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAARCAYAAADkIz3lAAAACXBIWXMAAAsTAAALEwEAmpwYAAABLUlEQVQokWVS0W7DIAzk/79se9o+YdIe1rQJYMBA0sBV9hrWrpYsATnfne0Y5zwoRGzbFa11tNY0e+94DDMvHjlnlFrBXBDzhpQ3bNcGoGuBpAmRQSEoy7JYeOex7/sA9AOYMytAgHaxsNbpWeIJKB6n80VZ5ss8MucywFJoQghIifVCnuA9Kauca6nYtbkO44N4jFop7JK/xQnruo4JGE+En9Ok0su8KOPh8Yi/ZqxToMjFmAZLv6dKU2DEGBUokpxYh380cSzBWB+GF2milAJZyjNjg0kxqpw8iEeZZa3r68BVjlkvRAGZ8/DYHhmJCKfpjFqrMsp4xMoLIwXCZZ71o7X2Ll2fPQqwpAJJ6cBZB+ccOFdw3XUrYzzv3594+/rAdt31dxOP/2VF+gZZlJaG9IsIoAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="501" height="871"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.80ac650.501.png" srcset="/assets/ideal-img/2.80ac650.501.png 501w" width="501" height="871"></noscript></div></div><p>생성된 token값을 복사한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAdUlEQVQImV2NbQrDIBBEvf99QlKjTUIv1IL4kez6igZ/tAMPdmYWxuzHi+d+sPqNxTqmeWW2nmlxzA+HdR7nNwx/emchCMT6mxsRoaGqlPPiEyIxZVIMlJwQ1d6bWisD1Uo5hfNSLtHum1rXp8fjrXa37Gb4L6tovjkC567oAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1920" height="937"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.e741a08.1920.png" srcset="/assets/ideal-img/3.e741a08.1920.png 1920w" width="1920" height="937"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="intellij에-token-등록">IntelliJ에 Token 등록<a class="hash-link" href="#intellij에-token-등록" title="제목으로 바로 가기">​</a></h2><p>IntelliJ에서 push시 올바르게 계정이 등록이 안됐는지 계속 Confirm을 요구한다. 여기서 토큰 사용을 클릭한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAa0lEQVQImSXLSwrCMBRA0axKQRO1r+nLhyZB8TOxFYeuQMHFX6EOzvAYG08MueBzxYni+oCVv02n2H3HukyYyzki3iOa6UPCa1zIoLiD4OyWVbph5PoiPb+E+Y3cP+zqg7E1xnZcYq0F22Z+CvQ3vYrLsWIAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="372" height="116"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.f334507.372.png" srcset="/assets/ideal-img/4.f334507.372.png 372w" width="372" height="116"></noscript></div></div><p>아까 발급받은 toten 값을 붙어 넣는다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAfElEQVQImT3GTQ6CMBCA0d7KCBXsMPRnKFAxkrj2/mf4TFy4es/5eUWTETTSj4F+FLoh4Ef53d8nbkFxIRqlvcjtRMqTsu7U42SyB7EexLKgKeO8RDQbqTY0bxQztnaguRLzwpwKMkfcVTLdKFwH+XsZJvz+wdsbtZWQFr4KU0iZuX+wGgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="424" height="188"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.f3aac77.424.png" srcset="/assets/ideal-img/5.f3aac77.424.png 424w" width="424" height="188"></noscript></div></div><p>이후 정상적으로 push 되는 것을 확인할 수 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAL0lEQVQImQXBiw0AEAxAQbsRCRpCW5/9B3nuQhyO7sdSw/yw1PFzmWqUJlTpxJT5h3UTYAMmhdAAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="351" height="58"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.791cfdc.351.png" srcset="/assets/ideal-img/6.791cfdc.351.png 351w" width="351" height="58"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="이외">이외<a class="hash-link" href="#이외" title="제목으로 바로 가기">​</a></h2><p>또한, 이전에는 내 개인 repos만 출력되었는데 이제는 Organizations까지 모든 repos가 출력되는 것을 확인할 수 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAArklEQVQImS2Ny27CMBRE/U0pSBWpH9fPxEBj2iLED0AWrPv7p8JlMRrNzJFGxf2JUComZFwsGJ+wIfdsQkL7iKQJ1X4upKniU6EePil1T8wT2nk+rHRpKygtsQcjoYP1uBDL3MHRCKNxfVOji714Xs7HhemwEPLMzshLTzCitNbocYczmjoXcgpELwTvCF4QZ/FiUUNbGdqNod15a3c2p5XN1/rv3w+211/ezw/+AHC/bHAkvPW9AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="802" height="461"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.fe7eaec.802.png" srcset="/assets/ideal-img/7.fe7eaec.802.png 802w" width="802" height="461"></noscript></div></div>]]></content>
        <category label="GitHub" term="GitHub"/>
        <category label="IntellJ IDEA" term="IntellJ IDEA"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[GitHub 프로필에 나의 Daliy 코딩 시간을 표시하기]]></title>
        <id>/2020/10/12/applying-my-daliy-coding-time-to-github-profile</id>
        <link href="https://parkgang.github.io/blog/2020/10/12/applying-my-daliy-coding-time-to-github-profile"/>
        <updated>2020-10-12T03:00:00.000Z</updated>
        <summary type="html"><![CDATA[GitHub 프로필에 주로 언제 코딩을 하는지 나타내면 어떨까요? 밤에 주로 활동하는 올빼미형 인간인지 아침형 인간인지 유추할 수 있을 것입니다. 해당 페이지에서 commit 일자를 기준으로 GitHub 프로필에 나의 Daliy 코딩 시간을 표시해 보도록 하겠습니다.]]></summary>
        <content type="html"><![CDATA[<p>GitHub 프로필에 주로 언제 코딩을 하는지 나타내면 어떨까요? 밤에 주로 활동하는 올빼미형 인간인지 아침형 인간인지 유추할 수 있을 것입니다. 해당 페이지에서 commit 일자를 기준으로 GitHub 프로필에 나의 Daliy 코딩 시간을 표시해 보도록 하겠습니다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p><a href="https://fernando.kr/develop/2020-05-02-github-gist-posting/?fbclid=IwAR2Xxg1VGXn-4xa1b3pZUbiTIp_nRsmO2ukHfsYffydhe_GYHUhf6A3LbpE" target="_blank" rel="noopener noreferrer">FERNANDO 기술 블로그</a> 참고하여 작성되었습니다.</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="repos-포크">repos 포크<a class="hash-link" href="#repos-포크" title="제목으로 바로 가기">​</a></h2><p><a href="https://github.com/techinpark/productive-box" target="_blank" rel="noopener noreferrer">해당 repos</a>를 <strong>자신</strong>의 repos로 fork한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAeUlEQVQImUWMAQ7DIAwD+//vVO2AdvtUCQyFcFPopkWyFNuXLHt84jrOFyGdPOLBFhJrCOwhse2RuG4sfKeP3wYKXFr/AbCYdd5NkVLJWchSaO6l0HvHzFDV+6P2TmttBl64cilIrYwxpp+gG7/OIlxXvmHPzLyd4AdPar1BSWql1QAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1920" height="937"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.b056b69.1920.png" srcset="/assets/ideal-img/1.b056b69.1920.png 1920w" width="1920" height="937"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="gist-생성">Gist 생성<a class="hash-link" href="#gist-생성" title="제목으로 바로 가기">​</a></h2><p><a href="https://gist.github.com" target="_blank" rel="noopener noreferrer">링크</a>로 이동하여 새로운 Public Gist를 생성한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAiElEQVQImU2NMQoCMRBFc29FUEzi6go2Fh7GK1jYWQjaLVaChSzOzJMJ2WUHHjM/kPdDPpxYtcfCLO6Zxy2L2LBMmfUmk5rIrk0E6sgEGx4nE0wVR0VQlbo9GyioWiGYgZmhfritbnd/fu8xl2oP4qb6wRQut47z9Y6o1uqJacCrnq8vj64fjX8S7t7/08pEmwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="905"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.9cfaf01.1680.png" srcset="/assets/ideal-img/2.9cfaf01.1680.png 1680w" width="1680" height="905"></noscript></div></div><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>여기서 생성되는 gist를 출력되는 껍데기 이므로 제목과 내용을 마음대로 작성하면 된다. public으로 저장되어야지 추후, pinned에 고정이 가능하다.</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="github-토큰-발행">GitHub 토큰 발행<a class="hash-link" href="#github-토큰-발행" title="제목으로 바로 가기">​</a></h2><p><a href="https://github.com/settings/tokens/new" target="_blank" rel="noopener noreferrer">링크</a>에서 <code>repo</code> , <code>gist</code> 를 선택 후 token을 발급받습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAPCAYAAADd/14OAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA/0lEQVQokW1SW27DIBD0/c/TA/QS/coV2gQI7ANYT7U4oKAWaSTLnt154EOkQkXRe4fWCq0KM4Of8zwXDiZCaw0sgpQSUsooxGBR1DFY0XrHYb2PiUKC+GSwAkUMJDbez3PMB2ZFKQRVBRFDtcPepedUrQ05ZwgL7j939NY3n4vogWJMEBGER8AzPYd3/+7hFpGZkXNB1Yr4CIghouQy2tg2+qa50UmtNpj949FDTGKKaW2yFxaRWAZG6kLo3Ya3iU3aS3diinEF+SPttbi0p9+k7cLmMYQIIhr1+MDyuBFFX/UoUojjplq7rnEQ7bz+lK/vGz5un6jtMv/uzz3+AlRSTFXR3aYEAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="520" height="758"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.67b87d8.520.png" srcset="/assets/ideal-img/3.67b87d8.520.png 520w" width="520" height="758"></noscript></div></div><p>발급된 token을 복사해 놓습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAkElEQVQImS2PSQ7CMBAE/f9vQkDkQIQTb7MVsmGk0lxavSR3Z56bMXrDVAi3haogvaMipForZkrpwn5cvM/GcQ0+1chVyFXp4iQzIwhyLtzuL7Zt57btbEfmkQvPs/FpQlq5M1oF6xUfbRGhgGE20N5IEQHTMwL3wP/fzBcqunr+haCqlCqUJgxR3Kfbjzn4C46Y6sUWVh5oAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1040" height="589"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.c98f7b1.1040.png" srcset="/assets/ideal-img/4.c98f7b1.1040.png 1040w" width="1040" height="589"></noscript></div></div><div class="admonition admonition-caution alert alert--warning"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>caution</h5></div><div class="admonition-content"><p>해당 page에서 복사하지 않으면 해당 token으로 접속해서 재발급 받아야합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAARUlEQVQImQXBSQ6AMAwEQf7/SBQhcYGbI2cZGzdVx1wbSUib4cYYRsaiKqhP1J5UikN7oUhCHbcT7w23xvSLXDd6GmkvP365TYTWoLF6AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1057" height="241"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.98de283.1057.png" srcset="/assets/ideal-img/5.98de283.1057.png 1057w" width="1057" height="241"></noscript></div></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="repos-환경-변수-추가">repos 환경 변수 추가<a class="hash-link" href="#repos-환경-변수-추가" title="제목으로 바로 가기">​</a></h2><p>아까전 fork한 repos에서 <code>Settings</code> → <code>Secrets</code> → <code>New secret</code> 으로 들어가 환경변수를 생성합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAZklEQVQImV2MWw4DMQgD9/7X3RJeSaYyX1WRDALGfl5L3pVkFVFNfIzKovchInFbZDXPvZespLtZ7iwzfPlAlUlHoHrUwoy9NzLJ4BG4B+ecgXQfUE6B/yVAmsTzs2gqZY/uSH+BX6HMxRnZ/0ZNAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="845"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.7665967.1680.png" srcset="/assets/ideal-img/6.7665967.1680.png 1680w" width="1680" height="845"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="gh_token">GH_TOKEN<a class="hash-link" href="#gh_token" title="제목으로 바로 가기">​</a></h3><p>이름을 반드시 <code>GH_TOKEN</code> 으로 해야합니다.</p><p>이전에 복사한 GitHub token 값을 붙어넣습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAZElEQVQImU2NUQrDMAxDc/+z7mMsHYllyypO2lLDs0AIqfW/qTC4ZjFN8JBHbM+wtGWmAJO7a/RD9uviGCq/uK/Vm2aKCAEQ7MLjaX6CFajGz/gK4WKmglyQu3VN3zC5lS+u+RO5d8UauDwkJAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1679" height="847"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.cfc3aa3.1679.png" srcset="/assets/ideal-img/7.cfc3aa3.1679.png 1679w" width="1679" height="847"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="gist_id">GIST_ID<a class="hash-link" href="#gist_id" title="제목으로 바로 가기">​</a></h3><p>이름을 반드시 <code>GIST_ID</code> 으로 해야합니다.</p><p>이전에 생성한 Gist의 URL 주소의 뒷부분을 붙어넣습니다.</p><blockquote><p><code>https://gist.github.com/사용자아이디/[이부분을 복사]</code></p></blockquote><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAbUlEQVQImU2N6wpCMQyDz/s/qz+ODLfeP+0maCGlTUJyPcfiHgtRZYky50TVMHNEbXOtXVWFmeLuyHhhY5BLaL74zVUFIkJEoHaSNrx/xyOPsZeZ7cTHvPEIMovI3Oj7m1in5g8tHnORXfkxvgGY/8TvLP7XmAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="847"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.3c34405.1680.png" srcset="/assets/ideal-img/8.3c34405.1680.png 1680w" width="1680" height="847"></noscript></div></div><details class="details_rxUF alert alert--info details_PYSW" data-collapsed="true"><summary>gist의 URL 주소를 까먹은 경우 아래 방법을 통해서 다시 조회가 가능합니다.</summary><div><div class="collapsibleContent_oXwz"><div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAWCAYAAAD5Jg1dAAAACXBIWXMAAAsTAAALEwEAmpwYAAACIElEQVQokXWSu2sVQRSHt7S3EBSM0Sg+iggmICksRNDGf0PwRYJBQlBstLBU/wdBUEFBEcFKO4kEC7l5kJgQyc29u3t3dnYe+7yfzORBTPAHvx2W+c6Zc+ZMMDB0hsFT5zh28iyHB4Zw/0dPnObI4CmGR8f83qXLVwnOj44x9eARE5NT3Bmf5P70Q26PT3Lj1l2evXjOlWvXGR65SPBj9id1XdNLEmyeU1UVu9WaX+D9h08EdQNR3GNhcYnl3yt0ul36/b4Pruuaqm5ogCAWkqoqMDqlKErvTaihaRqUtqRSEdiywciQtdZX0kwTRhF5XvisTdOgbU6mLUEUJ1RV7V1WlV8d4OzgHbAbxmhjEEL4bEpp34SDnIwtNkEXoZQiSQRRFHsnSbILzLdAk2OMRWtNmkqESBFpSlmWe0BbIGVG3Etob3S8HegactqpsdONqMocqzOyVJCbzRq35QI8uNYOWe2kzK+GzK10CWVJT9XEqiJWNT1p/V0GL7+tc3x6kQuPlxh5ssyhe/McnJjz64GbLZ5+bEO/IFhsK97NSr60NJ9/Kd7MCF7PCN7OpLz6LphdkeR5TmCMgdqiRERlJXtVVwVSWYI/6xvITNPe6BL1EkSaobSh7kPd9HHX55txG1mmCMPITyiVEn/KlnYmY/LCT0bKf4/97wg7na6fzDa0D3QZXbbIPwi1rxm370H3cXXGifAPVBm7Y22sh5TJ+Qt1UBt/pAOUywAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="230" height="515"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9.b8fb92d.230.png" srcset="/assets/ideal-img/9.b8fb92d.230.png 230w" width="230" height="515"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAeUlEQVQImW2Muw6CUBBE789rQWGhv0FFRy+JkHtJNGIHfyCNNAZ2j9klPgonmZ3MIxv6YSAvSjbZnro6cY6JS2qdqW5om8i1uxEAYjpy2G2Zn7PZvwiqyjQ9uI+jB4uIU35o3oeqfAIRZRF1NVrvH+2s4zWAt35h3QtsH73N9SyweAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1679" height="907"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/10.a0f38fc.1679.png" srcset="/assets/ideal-img/10.a0f38fc.1679.png 1679w" width="1679" height="907"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAh0lEQVQImU1MiwrCMBDr//+ITEXnNvGjpL0ORG3vEelNiwfhklwu4bjcMFwWnOcrDuOE/TR3DOOE3Wl0HlIk5LwiUfbdQA20IkYCJQJRRsB3nq83KgtYFSzqvLD8zghm5kREUUpFrexBFsH9kcAqgGFrbGFVRftp0KbNIO5tRb3Rx/mf7rbhA/ii4wGGiJ6jAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="971"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/11.f370ba8.1680.png" srcset="/assets/ideal-img/11.f370ba8.1680.png 1680w" width="1680" height="971"></noscript></div></div></div></div></div></details><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="action-등록">Action 등록<a class="hash-link" href="#action-등록" title="제목으로 바로 가기">​</a></h2><p><code>understand my workflows</code> 클릭하여 매 정각 업데이트가 되도록 활성화 시켜준다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAZ0lEQVQImV2NbQoCMQxEe//b6fpXPILuB2WTdvIkC5ViYBgmeUPKXo31ODFzzBunOd4areuXzZ0iCQjS1TvqkKuQiAjGlAwj7sfG8npwe975bO/sX3CqJDCa6dXqJUV+msAZ+p/59gWYZMTDHpdgJAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1678" height="846"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/12.c60431f.1678.png" srcset="/assets/ideal-img/12.c60431f.1678.png 1678w" width="1678" height="846"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="잘-등록됐는지-확인해-봅시다">잘 등록됐는지 확인해 봅시다<a class="hash-link" href="#잘-등록됐는지-확인해-봅시다" title="제목으로 바로 가기">​</a></h2><p>지금 당장 결과가 보이게 하기 위해서는 포크한 repos에 push를 해서 업데이트를 트리거 시켜야합니다.</p><p>간단하게 <code>README.md</code> 에 공백을 추가 후 commit push 하도록 합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAc0lEQVQImT3O2wrEMAhF0fz/lw60L5mmSYyXXUyhD4LiOmgBmHNy350xxu5FBDUjAnQt3IPiEXtR65/ravTEH3x37k7JVGuN4zw3krUYUzYOd1R1B0qezlTvg6WGmn+Vs5kneeE04VcPRNaH81z+lq8lfACAQ8RP9xoIVgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1679" height="904"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/13.43de607.1679.png" srcset="/assets/ideal-img/13.43de607.1679.png 1679w" width="1679" height="904"></noscript></div></div><p>업데이트가 대기 중 이였다가.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAg0lEQVQImX2NXQuCQBBF988bhluJ9t96S0uLXgKjD5x1dk+sGfVSF85wLgxcY/M1Ni8pyhK7KkjmltnIgiTNxp5mSwzA+eZpOx/1Z0zwyqlzbI4PLtc7fS+IOMS5yYUQAiaed3qREVXPoB7nBlT19fh373u62jfUzYGqaYke2e4m6g9PyvncetgjlSgAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="909"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/14.1f18715.1680.png" srcset="/assets/ideal-img/14.1f18715.1680.png 1680w" width="1680" height="909"></noscript></div></div><p>완료된 것을 확인할 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAbUlEQVQImW2MUQoCMRBDe//DKbtMWw8g/otLt60z86RbQQUDIbwEEhbJnJaIpExMF1aJSMwHr5IODw4Atzucr4r6oP8KqkpXo3XlsW2UstP6k9o6Za/U1jCz+QjzahSqhrvjzjvnFr7LX314bC+Yg8DxBbWq5AAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1681" height="907"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/15.f361172.1681.png" srcset="/assets/ideal-img/15.f361172.1681.png 1681w" width="1681" height="907"></noscript></div></div><p>이전에 만들었던 temp gist는 사라지고 업데이트된 gist가 생성된 것을 확인할 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAcUlEQVQImVVMCQ7DIAzj/1/rGNDuG5NYKUdVB0+hq6pZcmJHjs1kPadnYJhf9PNCF25a5/mwbmzDHwDwsyYCcp3+YESEImAuheuWmWtjbTtLbYw58QCoGdN7Hx9q9KiNGM+d7xIJwdmoQ8MnVd/+guovQdbARGCjoEMAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1679" height="906"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/16.96cd9fe.1679.png" srcset="/assets/ideal-img/16.96cd9fe.1679.png 1679w" width="1679" height="906"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="github-프로필에-pinned-등록">GitHub 프로필에 pinned 등록<a class="hash-link" href="#github-프로필에-pinned-등록" title="제목으로 바로 가기">​</a></h2><p>다른 사람이 제 GitHub 방문시 바로 나타낼 수 있도록 pinned로 등록하도록 합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAArElEQVQImSXMSwrCMBgE4FzGrWuv5MKNgg8QKqS1aV6NFkVLpYLFlYjHEBQpiPUkFsTFSNJ/NfB/M0SbJagfYDiaIM1y3B8lnq8K2yzHYDxFwASsISo24CqGH0ZI8wL19wd7HuVotTvo9vpIVmsLFxC6gafzBZ+6dtBmb0YhuYI1RGrjpiOucCiOqKo3ruUNm/0OQmr3s4YobVywLcYlaMDgzyOEXCIUEkw2i39rBowSIKa0iQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1678" height="907"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/17.86129ab.1678.png" srcset="/assets/ideal-img/17.86129ab.1678.png 1678w" width="1678" height="907"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="reference">Reference<a class="hash-link" href="#reference" title="제목으로 바로 가기">​</a></h2><ol><li><a href="https://github.com/maxam2017/waka-box" target="_blank" rel="noopener noreferrer">소개한 방법과 비슷한 제품류</a></li><li>개성있는 GitHub 프로필<ol><li><a href="https://github.com/techinpark" target="_blank" rel="noopener noreferrer">techinpark</a></li><li><a href="https://github.com/woskaangel" target="_blank" rel="noopener noreferrer">woskaangel</a></li><li><a href="https://github.com/Im-Tae" target="_blank" rel="noopener noreferrer">Im-Tae</a></li><li><a href="https://github.com/injoon5" target="_blank" rel="noopener noreferrer">injoon5</a></li><li><a href="https://github.com/du-dung" target="_blank" rel="noopener noreferrer">du-dung</a></li><li><a href="https://github.com/jiyeoon" target="_blank" rel="noopener noreferrer">jiyeoon</a></li><li><a href="https://github.com/anuraghazra" target="_blank" rel="noopener noreferrer">anuraghazra</a></li></ol></li></ol>]]></content>
        <category label="GitHub" term="GitHub"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[MongoDB Install 및 실행]]></title>
        <id>/2020/10/11/install-and-run-mongo-db</id>
        <link href="https://parkgang.github.io/blog/2020/10/11/install-and-run-mongo-db"/>
        <updated>2020-10-11T03:00:00.000Z</updated>
        <summary type="html"><![CDATA[해당 글은 Mysql과 같은 RDBMS를 배우고 오신 분이 실수도 있고 처음으로 DB를 접근하시는 분 이 실수도 있습니다. 초보자 시점과 Mysql과 다른 점을 집중적으로 적었으니 꼭 글을 끝까지 읽어보신 후 실습 부탁드립니다 😁]]></summary>
        <content type="html"><![CDATA[<p>해당 글은 Mysql과 같은 RDBMS를 배우고 오신 분이 실수도 있고 처음으로 DB를 접근하시는 분 이 실수도 있습니다. 초보자 시점과 Mysql과 다른 점을 집중적으로 적었으니 꼭 글을 끝까지 읽어보신 후 실습 부탁드립니다 😁</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="mongodb-install">MongoDB Install<a class="hash-link" href="#mongodb-install" title="제목으로 바로 가기">​</a></h2><p><a href="https://www.mongodb.com" target="_blank" rel="noopener noreferrer">MongoDB</a> 에서 접속 후 상단의 <code>Software</code> → <code>Community Server</code> 를 클릭한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAjElEQVQImU2NbQrCMBBEc/8DeQD/iGi1tWl7AwVtTRtMSz6aJ0kRfDA7uzDDiqKqOR+OXMsbVdNR1JKLbKhkSylbirphd9ojALy1yYjAfXjxePf5/kf4EDDzjHUuJ59GMSxT3l3whHXFeoeIMfJTKvWfkcFowhq2LzGmsQUTxhjUqFBqzJq0zsWEWjRf/S2+8gam3nsAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1920" height="936"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.d0c4937.1920.png" srcset="/assets/ideal-img/1.d0c4937.1920.png 1920w" width="1920" height="936"></noscript></div></div><p>자신의 사양에 맞게 버전을 설정후 Download를 클릭한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAg0lEQVQImUWK2wqDMBBE/f8P7IPgQ0HjpVJtTBR3NznFWOjAYWDOVKcIZoaI4PfAEjw+Bvwey66mNEtLBSCqxOP4CSudUkJFLw2W7uMWIuNr5uM96+fGbxvz+kZEEZX7mAG1jKWMpkzKEM6Dx9QUd6UaB0ffO7quxbk/XaFjGnrqZ80X7QPA3R5qbFkAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1921" height="939"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.75cb857.1921.png" srcset="/assets/ideal-img/2.75cb857.1921.png 1921w" width="1921" height="939"></noscript></div></div><p>Next 클릭</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAABLElEQVQYlR3LvS8DYQCA8RstFrFZWHRqgpgMBolEDDW0IdJWU3HBwir+iQ5WiUQQUbH4Wmw1EAYnpR96rVN39779ump9tA19hOlZfo8yshxhSN1gSI0Q3Lxh5TiPGtWZ3csQ2NcJR3P4dnQUdaCTpdEu5lwK2sUWn9UU7wWNuryjJjX4zmI6Fkpoop/FXT/e8R6uzndxagaW+YAQSaRMUZaP3BnPKH51kNBJAE/YxfXZNs5bDjOv4ZSfqDo6jbpO/NVACfpcTB94mfL2kogdAQ34FkAR2oX/pqWFMj/czULQzYy7g/jlIfDBT8uCH0H7f5Ck/uCaZ4D1STerY30kb0+BOs0vk1ZT0GwIaAsStoli2zYFaSOFRaVSplQqktUz3KcNHnM2wnwhFjf4Bebd+/JWwjOUAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="495" height="387"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.8502c4a.495.png" srcset="/assets/ideal-img/3.8502c4a.495.png 495w" width="495" height="387"></noscript></div></div><p>동의 후 Next 클릭</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAABBklEQVQYlT2OO07DQABEfSKOwgEAIdGk4AR03ADRUIMQEk2gpqag4KMIhMLPjr0be2N71/EvxAsyechGYqSpZvRmnMHZiI2TR7aOR2yfPrF7OWEw9Ng8d9m5kKwf3bC2N8SxdUCtx9T6hYV55buaQCOBBKhIxlccHO7j6HnBsrHIaciH6/HueohpiDZzysWSZvnJrWtw8rKmU5ZlGGPQWhPHMY21rPqk5X5icCIVE4ZTgiBACtHb933SJKUoS+yi4qErZnlJ27YUeY4xuqemacqXtfysOmb7V1SzBKUUURQhOqKU/fw8z8m6/1XBnZfipCb7/9fNR0ohhGTsK95kwiyUXD8LfgGs+iCeKzcTiQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="495" height="387"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.19b73c4.495.png" srcset="/assets/ideal-img/4.19b73c4.495.png 495w" width="495" height="387"></noscript></div></div><p><code>Complete</code> 를 눌러 설치합니다.</p><p>기본경로 <code>C:\Program Files</code> 밑에 설치하고자 한다면 <code>Complete</code> 다른경로에 설치하고 싶다면 <code>Custom</code> 을 선택하면 됩니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAABAUlEQVQYlT2Mu0oDQQBF94v8FHux0UZBf8DSLxCL9BYiNkZrGwsRLcwSIRofOzPZR8w+ZnZmWLKioB7ZCCkOtzn3BFsnQ9aOHtg4HrF7odg5l2z3Feungs1+ymrvlpW9M4KveUJrXvj0gp824fcjge8ZUAIt1fMlB4f7BGVhGN2EDK7uSGVCqQ1lpSkqTV5q5k1DOHEEtfWI14hhOCR6i0izjDwvyPOc6fuMtnEMlCaonSeOY57GY6RUCCGIIoGUkiyb4kxJqAyBsQ7nLEYbtNZLjDGY2tI2/l/UtcP7Dr/Eue7sqG0nOu5l1Yl2UbS2W7coTeIYpSbESUoxTbl+TPgD1/UePl9isUkAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="495" height="387"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.e7fdfe5.495.png" srcset="/assets/ideal-img/5.e7fdfe5.495.png 495w" width="495" height="387"></noscript></div></div><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p><a href="https://javacpro.tistory.com/64" target="_blank" rel="noopener noreferrer">Custom으로 설치해보기</a></p></div></div><p>설정을 변경하지 않고 기본 값 그대로 설치한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA/klEQVQYlSWOsU7DMABE84NsDHRiKPwUQ6cOlJ9gRupQQKVp6jhO7aSJ7TpxW6BISA81DKcbTnrvktvJguuHN0aTJfdPivFjwd1MMZ6V3EwVo6niaiJJbK1pyg1WFxz9jt5WQ87R8/vZw08k1Z5kleUsP1as0gyRF7TOE7oeHzrsPnCIHS/CkexayzpNKZRivc5QStG2LSF0hK7j69gzl57E7cMwOOdorcV7TwiBGCN9H/k+RubSkZRbQ1EUaG3Ic0lVVRhj0FpjTMUphn9itWuo65qmaZBSIoQg2wiEyFHllvPpwDx3JM4HuhAG3aUvVxbZlvfsYtHY2vD8KvkDbQMe7syHZpEAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="495" height="387"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.70fb65f.495.png" srcset="/assets/ideal-img/6.70fb65f.495.png 495w" width="495" height="387"></noscript></div></div><p>GUI기반 MongoDB Tool을 설치하실 분은 <code>Install MongoDB Compass</code> 체크하고 설치하시면 됩니다.</p><p>해당 페이지에서는 <code>DataGrip</code> 을 사용할 예정이지만 나중에 살펴보기 위해 이번 편에서는 설치하도록 하겠습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA70lEQVQYlW3NPUoDQRyG8TmRRxFsLIyFjXew8wZiYydaCGqRE1gGTZNkP5KoEbMZZ40iu7Pzn91lo2jxSBAkiMUP3uKFR+2eBbROIzaPQzZOxmxfJrQupmydP7LTNqwfdVnba6MWpca9DqmyWxp7z2c55atK+JAHoMBOrjg43EclOiWMYsJoSLAUxoTxiEEQMbqb0NQVAy2ovHA4t0Lkd9vC0VSe/ixHWSd47//lxLOoPf1keRSPLz3if/w9vtclg5lF5VmOWIvkFnEOWUkXbpkWekmGMr0AfdPlqXONNYZCBK01xqQ8z194m6d0xoZvQj0dxdu56jgAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="495" height="387"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.da29c30.495.png" srcset="/assets/ideal-img/7.da29c30.495.png 495w" width="495" height="387"></noscript></div></div><p>Install 클릭하여 설치를 진행합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA9UlEQVQYlVXNMU7DMBhAYZ+Io7AyMjCgLsxs3ACxcAMmCBdgZOhCQUJCVVPHIW0DjhLH/uOKFsrwqpQuDG/7pKfOkjGniWGQaAb3GYMk4+RWc3STcnxXcHg95OA8QX0vC36jYS2aTTRsYs7Kp/zIFPC4yQOXVxeoWWmZpFN0lqMzw1QbinnJh62YlZ9EEV4KQVW1w1qLrSqqfXVd45yjbhxfMTDKG1QbhBgjXdf9S0QIIqyWwsg0KOcDIoEQ/upBX9fDEFgvO55zh2ragOxRn/ee1nsa19K0frd+MnUPPSH4HejhYrFAFyXD15Txu8WWcx7fZmwBVjcgZYI0DLIAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="495" height="387"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.72d85a1.495.png" srcset="/assets/ideal-img/8.72d85a1.495.png 495w" width="495" height="387"></noscript></div></div><p>이전에 체크한 <code>MongoDBCompass</code> 가 설치된 것을 확인할 수 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAABVUlEQVQYlR2QTUtbQRiFp7sK3ZVSBDe2UDdiBZuYmXfembm5N1+119jGkkVIQPohaUEjDVZw7U60f6Du3bhyJd1IoTt/01OS1dkcznPOMUXe4OPugPeNgqYGYozkeUGIGTEFqu1d3rwdYmLWoDc6YjsqhXiseErdpJXX0a0Bzw7vWPj5gJEQSOU2XhUrDvGB12nIy+4JT3785cXoF2u9CcbFOq4cEMQiIjinrORjFnvnPJ7cUyn3aEWLUQ102l1CUMQ5RAOr/VM2+lPy/hdsamOtx2hW8G44Js8S6oWKJF6dPbA8vWWld8xG6FJd38SkvEGru4N6j3eWmmY8Pb5nYfqPR9//8Onwgp3BZ0zW7FDufaOe4hxtNbG8/5vn42uWDm5oTa6I40tMaG5R+fCVTB1OPF4EL26eHqVG1QmVWcfUbNMZ7RO8Q2ZG7+fr5w/M1DmcrfEffHu5nyfs6REAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="646" height="641"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9.2e4bc44.646.png" srcset="/assets/ideal-img/9.2e4bc44.646.png 646w" width="646" height="641"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="환경-변수-설정">환경 변수 설정<a class="hash-link" href="#환경-변수-설정" title="제목으로 바로 가기">​</a></h2><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p><a href="https://somjang.tistory.com/entry/WindowsMongodb%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0ver-420" target="_blank" rel="noopener noreferrer">[Windows]Mongodb설치하고 환경변수 설정하기(ver 4.2.0)</a> 을 참고하여 작성되었습니다.</p></div></div><p>설치 후 <code>mongod</code> , <code>mongo</code> 와 같은 실행파일을 커맨드라인에서 경로를 들어가지않아도 편리하게 바로 실행할 수 있도록 환경변수를 추가해야 한다.</p><blockquote><p>그냥 어디서든지 명령어를 실행하고 싶어서 그렇습니다 😏</p></blockquote><p>먼저 mongodb가 설치되어있는 폴더의 주소를 복사합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAbElEQVQImT2JWwqCQAAA9/7X8ACJC51h7asfC1oSdA0Uk801X+g6kUHzMwMjAmWQcc5BFYSq2B2dHsjzE3lxHK+OKGkRAFtvYWgBj+8sjA789F1/RNrM6KpDV2+MW7mVv77XPZmdMa+FtJn4AAGibt0wuyFLAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1103" height="320"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/10.8595746.1103.png" srcset="/assets/ideal-img/10.8595746.1103.png 1103w" width="1103" height="320"></noscript></div></div><p>환경변수 설정 페이지로 들어갑니다.</p><blockquote><p><code>내 PC</code> 우클릭 → <code>속성</code> → 왼쪽의 <code>고급 시스템 설정</code> → <code>환경 변수</code></p></blockquote><p>이후 <code>Path</code> 를 선택 후 편집을 클릭합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAABLElEQVQYlVWOTU7CUBhFu0EHmqAzt+QOJIGIJi7AmQkTRX4MAVHAthRoaSlt33ulFIroMRRNdHBzki8n97vaecXiuKhzUjQolC1Oy2MKpRGFkpXzrGxxdGmiiblNJn3SyCVTC3ZJCB8rPpOIr7WCXczADtHGtssySXFcj8FQzzPz/Dx+KIiV5ElfoPlBiOfN0Q0DyxpjmiMMw8RxZggpSZeSprFA8/w5SSxIloosS9nus10jREQYCda/YvP5nbvqkOqjzkNzRL3jUGtNGJkO8m/j1PYYGlPa3T69do9+rUWv88bUnv0XQ6HyN0EYsJj7vL4MMa0JtuMQRIIkltT34iqWZIkiWyk2icq3prFkvTzsY6PojgO0q7rLdcOl0jjw5of5re5y2/K4uLf5BsKoZpzUAnomAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="618" height="585"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/11.ceeb9fb.618.png" srcset="/assets/ideal-img/11.ceeb9fb.618.png 618w" width="618" height="585"></noscript></div></div><p><code>새로 만들기</code> 를 클릭하여 이전에 복사한 경로를 붙여넣고 확인을 눌러 설정합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAYAAAALpr0TAAAACXBIWXMAAAsTAAALEwEAmpwYAAABBUlEQVQYlUXNzU7CQBSG4bl9gYXRm9BbABNcGJeIyo9pp+10Wjst/aGFVrSQvoay4Eue8y3OSY64ew65nwaMnjSjiWY48RmMFYOxz+30m5vHN4YPM0SxMSTaoSkSaGughe730rQkq3eS5RzhhzGWLbEdF1u6mHhDlhcc/tre2rKxPIXQYYTnuEhb9pRS+L5GByFplhNojdYaEURxv5TSYV/XHE8nutP57ZGuPeA4DlJKhPJ81osFy49P1oslnm2xcg0vbsNMlf3hmcg2GU1RsM9zqjSlyTNCs+XV2jJ3cowxPVHVP5zT9fPalxzZ7XY9keZbqqqiLMurqsKLUr6UITKGOI75B1fgSizn4XbvAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="527" height="501"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/12.fae0a12.527.png" srcset="/assets/ideal-img/12.fae0a12.527.png 527w" width="527" height="501"></noscript></div></div><p>명령 프롬프트로 실제로 잘 적용되었는지 확인해봅니다.</p><p><code>$ set path</code>
<code>C:\Program Files\MongoDB\Server\[버전명]\bin</code> 보인다면 환경변수 설정 완료입니다.</p><p><strong>mongo server가 활성화 되어있다면</strong> <code>mongo</code> 입력 시 mongoDB Shell으로 넘어가는걸 확인할 수 있습니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA3UlEQVQYlU3PT1OCQABA8f1uTUP8GZcdFpOoVAwRYddVEGtMO3To1qUP/Bo9efhd3u2J/fDL1+cfvfuhbb5ZLc9X1Y1leULI0JIE5ioODCq0SL9FeTXJwxrtN6j7FaLIPqiyd+a6Z6o7ZrrnWTleR5ZCWebSkAc1Yjk5UE8GymRLoRyF2lzNpOElqsmDijxYIXbaMjztGbKO4XFLP3ZcWp86utTh4oZdXCPevAVH3XIeG7rUUsuWU2o5jy0HWXIMp1RegdBewzwyyMBy512mNpQjRx5ZFuGazG9JfMM/JIKWsxAGs/AAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1235" height="846"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/13.76eaba7.1235.png" srcset="/assets/ideal-img/13.76eaba7.1235.png 1235w" width="1235" height="846"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="server-setting">Server Setting<a class="hash-link" href="#server-setting" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="개념">개념<a class="hash-link" href="#개념" title="제목으로 바로 가기">​</a></h3><p>대부분의 DB는 외부에서 접속하기 위해 Server를 내장하고 있다.</p><p>예를 들어 개인 공부 및 대학교 수업에서 Mysql으로 실습 시 설치할 때 잘보면 MySQL Server도 설치하는 것을 볼 수 있다.</p><p>그렇기 때문에 DB Server가 활성화 되어있지 않으면 DB의 접근은 불가능하다.</p><blockquote><p>필자의 경우 DBMS Install시 DB는 DB자체라고 생각했지 Server안에 DB가 있는 줄 몰랐다.<br>
<!-- -->해당 개념을 알고 접근하면 추후, JDBC와 같은 DB Connection시 이해하기 쉬울 것이다.</p></blockquote><p>MongoDB도 마찬가지다. 아래의 명령어를 통해 Server를 활성화 시키고 접근할 수 있다.</p><table><thead><tr><th>행위</th><th>명령어</th><th>설명</th></tr></thead><tbody><tr><td>Server</td><td><strong>mongod</strong></td><td>mongoDB 데몬의 약자로 mongoDB server를 활성화 시킵니다.</td></tr><tr><td>Client</td><td><strong>mongo</strong></td><td>Client 신분으로 mongoDB에 접속합니다.</td></tr></tbody></table><blockquote><p>보통 DBMS를 <code>mysql</code>으로 입문하게 되면 <code>mysql workbench</code> 편리한 GUI Tool 덕분에 익숙함에 모를 수 있지만 사실 백단에서는 mysql도 똑같이 shell기반으로 돌아가고 있다.</p></blockquote><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="활성화-전-mongod-이해하기">활성화 전 mongod 이해하기<a class="hash-link" href="#활성화-전-mongod-이해하기" title="제목으로 바로 가기">​</a></h3><p><code>mongod</code> Server를 활성화 시켜보겟다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAx0lEQVQImSXI2U6DQABA0fm2Rg1EgmBRGNlaS6lgQxlmmC6S1uXDr0n7cB/OFU39Q7M6k8mRLDFk0pAm+mZpru/tdUBEXkccDLz4PaHTXvPvNwROS+A0+A8fPM4qRBXvqeWBKraUz4oy7CmCHYu5YhkNlKGieOoQOj9h3y/ocqKXFpMfUemBIT8y5Cd08cVnpBBWGqZ8z19hmRKFjjWX1PBdWH6LkXM2YoINYu5u6fyWhVvjzVYkdxVbt2btrtl5NaXb0DhL/gHVHYGN3bmEswAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1010"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/14.8b62a31.1680.png" srcset="/assets/ideal-img/14.8b62a31.1680.png 1680w" width="1680" height="1010"></noscript></div></div><p>log가 쭈우욱 찍히더니 마지막 cmd를 보면 server가 실행되지 않은 것을 확인할 수 있다.</p><p>트러블 슈팅 전 <code>mongod</code> 라는 명령어에 대해 알 필요가 있다.</p><p><code>mongod -help</code> 를 입력해보면 해당 명령어에 대한 파라미터 목록이 나온다.</p><p><strong>우리는 argument없이 <code>mongod</code> 만 호출했음으로 Default으로 작동한 것이다.</strong></p><h4 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="db를-저장할-디렉토리-생성">DB를 저장할 디렉토리 생성<a class="hash-link" href="#db를-저장할-디렉토리-생성" title="제목으로 바로 가기">​</a></h4><p>server 실행 전 어떤 DB를 server에 올릴껀지 선택해 줘야 한다.</p><p><code>mongod</code> 의 DB 기본경로는 Windows 기준 <code>C:\data\db</code> 이다. 해당 디렉토리를 만들고 다시 실행해보자.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAYAAABhYU3QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAUklEQVQImWNYMH/1/1Urt4Lx1i0H/u/Yfvj/zh1H/q9cseX/lMnz/0+ftgiMGcyNI/5bmEb9NzeJ/G9tEfvf0izqv5V5zH9Tw7D/lqZRYDGQHACShy+rOPnwKAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="375" height="92"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/15.abd4a79.375.png" srcset="/assets/ideal-img/15.abd4a79.375.png 375w" width="375" height="92"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAXElEQVQImV2IyQpAUABF3//7BGX+Cx+gTKUUFrbkGVeG0FGPlVOn7rlC82vcsEUPJGY4YMeT0opGnGTGSxeMaEKguID7nYrz1yDydqXoNkp5UPWvpdyV1fdlzcoDyHFuLrHEZpgAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="882" height="253"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/16.8bcce83.882.png" srcset="/assets/ideal-img/16.8bcce83.882.png 882w" width="882" height="253"></noscript></div></div><p>현재는 빈 디렉토리로 되어있다. 이제 server를 실행해보자.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAx0lEQVQImR3F2W6CQABAUT6sTUlrDEYTgYEqOGzSioBUllGgGh7647fRh5OjFdkfh3QikgNxMJKEvyThSCR7Qv+K3Cq2To22MVsenNUP1qJELCvsRYllFJhGwXp+ZPXxjZbJGwdvIP28sHfVUyI6YtGQOC2hWRNaZ7RrMqGiO40cqf2eTg40cqD1e6qNonJbCnFGu3iK0euY3BO9lXNaF9xEyV08zhlFibKPaMu3PV+6ZKcHGC8B9mtA9h4S6ZJ8FrCbpWTzmH/r54BY2VI1QAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1010"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/17.3046682.1680.png" srcset="/assets/ideal-img/17.3046682.1680.png 1680w" width="1680" height="1010"></noscript></div></div><p>많은 log와 함께 정상적으로 server가 실행되는 것을 볼 수 있다.</p><p>잘보면 port <code>27017</code> 으로 실행되고 있다는 log를 확인할 수 있다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>MongoDB의 기본 포트 번호는 <code>27017</code> 이다.</p></div></div><p>또한, Default Value가 생성된것을 확인할 수 있다.</p><p>이제 <strong>해당서버에서 일어나는 CRUD는 서버가 지정된 아래 디렉토리에 모두 저장된다.</strong></p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAj0lEQVQImR2Myw6CMBRE+/9f5dqFwNaNikLAkFDoS9rbHlMmmWQyL3W5L7T9yvWhaQdPMziaj+P2tqfuxkA3ehQVyeH0AjGQD0cKpppAOeMKdUjGWMvz1Z/cjWHVG78jnkySkVxQpYA1O/M0I5IIwbNpTYqRLEKphfroY8Y4z/RdkDrygXUzhPoYE1EyIWb+FgHAG/2aiZgAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1015" height="548"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/18.3ae0ad6.1015.png" srcset="/assets/ideal-img/18.3ae0ad6.1015.png 1015w" width="1015" height="548"></noscript></div></div><h4 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="설정-정보-변경하기">설정 정보 변경하기<a class="hash-link" href="#설정-정보-변경하기" title="제목으로 바로 가기">​</a></h4><p>근데 port와 db 경로를 변경하고 싶으면 어떻게 해야 할까?</p><p><code>mongod</code> 실행 시 <code>--config</code> 및 <code>--dbpath</code> 파라미터로 값을 넘겨줄 수 있다.</p><ol><li><a href="https://poiemaweb.com/mongdb-basics#41-windows" target="_blank" rel="noopener noreferrer">--config 설정법</a></li><li><a href="https://javacpro.tistory.com/64" target="_blank" rel="noopener noreferrer">--dbpath 설정법</a></li></ol><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAq0lEQVQImT3OzQ7BQBiF4V4NbfUnaJBQNCFcto1EuAAbFuws2k5bmzIzr0zbOMnZPd/JZx1vKad7xvlRcXlWHK4vhtM9brDGDRNsL25qgUa/C1BfUB/ehSCadTBYYnsLHD/G+ipjJFIqTPKiJJruGYQJbrDC8Q2OsZTWaClRdd1AkWaMJlscv1000LSBJlq1iyIXDKMNfa8F/x+l6mB3IMqK8WxHz53/18ziD6vSpBF7C/iSAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1009"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/19.b5526c9.1680.png" srcset="/assets/ideal-img/19.b5526c9.1680.png 1680w" width="1680" height="1009"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="번외-">번외 🤬<a class="hash-link" href="#번외-" title="제목으로 바로 가기">​</a></h3><p>Windows의 경우 <code>MongoDB</code> 설치 시 자동으로 Server가 활성화되어있으며
Windows Service에도 등록되어있다.
서비스에 들어가면 확인할 수 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAApklEQVQImS3MS3KDMBREUfa/vGwgZYOQHIH+TzggdFPgDLp6crqHcX4xmoXFZ6xLn/jETxCMK9gg+CQM+74TYkJrjVITxmhKznzbwteUGP0bemfovZOLoJVifDyYnk9iCOSYCIulinC0dsHzhuvq/l8VORdSLqzOk0uhfWCnSMU5zzzPNxYR2nnyux8cR+MyQ62VECLWLhhjMC9Diolt23hv293X8A+EheTjO+TjdQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1920" height="1040"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/20.843462d.1920.png" srcset="/assets/ideal-img/20.843462d.1920.png 1920w" width="1920" height="1040"></noscript></div></div><blockquote><p>덕분에 내가 Server를 활성화 하지 않았는데 활성화 되어있어 훨씬 더 햇갈리게 되었다. 😭</p></blockquote><h4 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="어디를-참조해">어디를 참조해?<a class="hash-link" href="#어디를-참조해" title="제목으로 바로 가기">​</a></h4><p>사용 환경에 따라 다르지만 Local으로 사용할 경우 이미 Windows Service에 등록되어 있는 것을 사용하는 것이 편리해 보인다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>혹은 커스텀해서 Service으로 등록할 수 있다!</h5></div><div class="admonition-content"><ol><li><a href="https://joont.tistory.com/44" target="_blank" rel="noopener noreferrer">Windows Service 등록 방법</a></li><li><a href="https://devman.tistory.com/entry/MONGO-DB-%EC%89%BD%EA%B2%8C-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0-%EB%B0%8F-%EC%B2%B4%ED%81%AC%EC%82%AC%ED%95%AD-STEP-by-STEP" target="_blank" rel="noopener noreferrer">mongod에 Windows Service 등록해보기</a></li></ol></div></div><p>자동으로 생성된 MongoDB Windows Service를 까서 확인해보면 <strong>실행 파일 경로</strong>가 아래와 같이 설정되어 있다.</p><div class="language-shell codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-shell codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">"C:\Program Files\MongoDB\Server</span><span class="token string entity" style="color:#36acaa">\4</span><span class="token string" style="color:#e3116c">.4</span><span class="token string entity" style="color:#36acaa">\b</span><span class="token string" style="color:#e3116c">in\mongod.exe"</span><span class="token plain"> --config </span><span class="token string" style="color:#e3116c">"C:\Program Files\MongoDB\Server</span><span class="token string entity" style="color:#36acaa">\4</span><span class="token string" style="color:#e3116c">.4</span><span class="token string entity" style="color:#36acaa">\b</span><span class="token string" style="color:#e3116c">in\mongod.cfg"</span><span class="token plain"> --service</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>위에서 <a href="/blog/2020/10/11/install-and-run-mongo-db/#%EC%84%A4%EC%A0%95-%EC%A0%95%EB%B3%B4-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0">설정 정보 변경하기</a>를 보고오면 어떤 메커니즘으로 호출되는지 알 수 있다.</p><p>사실 우리가 이전에 환경변수를 지정하러 들어갔던 디렉토리를 보면 <code>.cfg</code> 파일이 생성되어 있었다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAb0lEQVQImT3JywqCQBhA4Xn/1+gBDAd6hslVmxQaEmoU77ffIkXthJvO6oOjDsahgyfeOeVoEjyT4Acp+lKjQ+EUCf51QAF82xy6chfb2MJHYJv29U/FzYzNBmwmOFm55SO2eHGv3jy6GdcvxM3ED/nfbrybDxo9AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1103" height="320"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/21.36f3dbc.1103.png" srcset="/assets/ideal-img/21.36f3dbc.1103.png 1103w" width="1103" height="320"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAMCAYAAABbayygAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA/klEQVQYlVWQWZKDMAxEfZOZFAS8GzBZCHsgTu5/oZ6ygQx8dHW59KyWRGLRoLi+wVUHKhow3Qc/swqpqJHwCimvQaLkAm0dZOEg8hk8n8HME8I8wVQPYUZo+wL5jS1upUNfOtTFjDqfUJkRUvcBpLKFMANIlF6h8wnGDGD8Ac4fwX20j034I3Qlp3OJ3DrY0kHpAVK1ELKFlC2obJCKZgF/ogL38o3h8kGVjYvMiLtP8Av6T6FjbCGLOSySyPYguoJhRg+qfILM/JYdmGpX78B28NIxm8IcX2CVBzY/guEk/8Be5BQV4GYIxT2wvTcRdXuB8gZUHQtc91/5w/8BPc69rraVLbsAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="840" height="1010"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/22.27ceb96.840.png" srcset="/assets/ideal-img/22.27ceb96.840.png 840w" width="840" height="1010"></noscript></div></div><p>기본으로 등록된 Windows Services를 사용하면 해당 경로로 log와 DB가 저장되는 것을 확인할 수 있다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="hello-world">Hello, world<a class="hash-link" href="#hello-world" title="제목으로 바로 가기">​</a></h2><p>Server에 대한 개념이 길었지만 해당 챕터를 이해하면 추후, 다른 DBMS 사용시 많은 도움이 될 것이다.</p><p>자 이제 Server를 열고 <code>mongo</code> 를 통해 접속해보자.</p><p>당연히 Server 활성화 안하고 들어가면 Connection 되지 않는다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAwklEQVQImSXIzU7CQBhG4bktXeACW6Z/E4sSQqCtJDA/hUprFWQBJhq37rzeQxgXT873fmL//sv59Meu+WFbf9Fsvr3r7cwnTp+x6xNinO7Ioy0qtP8C4yvvltzfLhjezD1R5i2FapiONJNwxSRY+V73LDa+U6kRJrXo1GAyh80ctaoxnkNntf+vU4twoyUvQcExquijCj0s2MuSQ1RxjAv66Jm3uEQMkg+e4pY86RjIjlB2zLJXHpOWuepI1YHFQ88FI8t90xYIMLgAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1235" height="714"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/23.b1d12d7.1235.png" srcset="/assets/ideal-img/23.b1d12d7.1235.png 1235w" width="1235" height="714"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="db-list-출력">DB list 출력<a class="hash-link" href="#db-list-출력" title="제목으로 바로 가기">​</a></h3><p><code>show dbs</code> 으로 DB list를 출력합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAy0lEQVQImR3FXU+CUACA4fOr6oZNHWg1IYQs+ShYtAOcgxhKYJO0dd9Vf/ht4+LZIz67X87DH7X+QecXdHGhKr9HSp4p5RfF24BwF4rVbYUzL3CsHNvKuZtmLIyE2XXI7CocF8/3W7LHD7J1S+o1JF7Di7sjWe1I/XeipR4J5WqasBsd4p427tlHR/ZRz3bdUDolclkger+m82sGT3H0FMouOXmak18xuJLDUtLaGcK5kaRmTDAJsIwNtrEhnQaEkydezYAHMyGbR/wDrPV+wrHS9Z0AAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1235" height="714"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/24.c21734a.1235.png" srcset="/assets/ideal-img/24.c21734a.1235.png 1235w" width="1235" height="714"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="db-생성">DB 생성<a class="hash-link" href="#db-생성" title="제목으로 바로 가기">​</a></h3><ol><li><code>use [DATABASE_NAME]</code> 명령어를 통해 Database를 생성합니다.</li><li><code>show dbs</code> 으로 조회해도 최소 한개의 Document가 없어서 출력되지 않습니다.</li><li>한개의 Document를 추가합니다.</li><li>정상적으로 조회되는 것을 확인할 수 있습니다.</li></ol><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAw0lEQVQImVXF3U7CMACA0b4UxMSEKEbXdT9haqQTxsBsbWeQrM44CXChL+CFD/wZ0RsvTo449J987L7wj++sqx1P9R5XH/6x1R6RqzXZuCI6nSNP7okGd4wHU86GmvOhPv5DPFx7VlnLcrJBB4ZZUKOlQYeWXDmm0h4JGztMbDGxwyWOJm2wyS/zdx07RBOs2FwWbMOSLiwxFwWvckEflmxVQRcueVELxCh641a1ZJFnJD1X0pMnz9xELbPUE6c980nHN3vffQCRedcbAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1235" height="714"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/25.6a29938.1235.png" srcset="/assets/ideal-img/25.6a29938.1235.png 1235w" width="1235" height="714"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="datagrip-연결">DataGrip 연결<a class="hash-link" href="#datagrip-연결" title="제목으로 바로 가기">​</a></h2><p><code>+</code> 버튼을 클릭 후 데이터 소스에서 MongoDB를 클릭한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAPCAYAAADd/14OAAAACXBIWXMAAAsTAAALEwEAmpwYAAABXklEQVQokW3R3W7UMBCG4dwR6gGiiZM4/nccO9mUUG1btBWccAEUqXDzL9rAAhJ7YNkaPRrPZ1c2roSUUdZj/IhyAeMj2gW08yjjCGmi6s+HGOmlpBYNTSuoRYvUjsF4lA28fVdTubHg5kd8ecDPT+h0ZHAT1hmMtfSD5ObmDdV4uCefXplPP4iPr9gPL5inr+TTd+LxG3p7Qc5fqAatmabEOCWc9whR04aNtH2mbM+Md88InamkDZTtSFlW4lRQNhKSZ15myjyjreG2rqlaZZAuIrWllYqmU4zZkUrGj3l/iaaTVKIfEL3kvF9gmnqmMu2oV4a67X7BM7gs0Ul0npmWFW393qAWv+G/uGk7wuEjaXmPtm6vXYWiG1CjJ+aCCyPdoK/D235gU5q1LNiY9u88Z/j/6q4njgeWdcP4cH1GccZdR5sf8HnFOr/X/qT+21Ej2ha1fCId7nd4CfMTYZ0ORgxmZksAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="367" height="565"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/26.0af85d6.367.png" srcset="/assets/ideal-img/26.0af85d6.367.png 367w" width="367" height="565"></noscript></div></div><p>만약 MongoDB Driver가 없다면 설치하고 Host와 Port를 입력 후 연결 테스트를 클릭한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA0UlEQVQYlT2OSU4EMRAE/a5ut122y0uvs4lBzIHTIPH/DwQagziEUqmMQ5rl/k27PZHlgeRGbBs+zx2nM6MPWFFMujxp1y/S/kl4CVHxMTNJxDrBvkQfMNpOhLwiuiCpoHUmloZoxoWEJO1p2vHOfNyJ9UzQQsiFWCqp1N7/xbK9kecrXndCyuzLRimNKSR8/KWLdb8j+cDGBecCR8qojwxOGF1g7CmY9fyBtgsurv38MPk+WOexk2N0nnHymLTekHrCppVBlPGPIVSsbkypdn4Ap/qP58UXC3MAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="802" height="677"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/27.62ce640.802.png" srcset="/assets/ideal-img/27.62ce640.802.png 802w" width="802" height="677"></noscript></div></div><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>접속 Server에 따라 계정 정보 등 필요한 값을 입력합니다.</p></div></div><p>정상적으로 연결되었다는 피드백이 있으면 적용 후 확인을 클릭한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAz0lEQVQYlT2OSWoEMQwA+1292LIlL73OEqZDcshpAvn/ByrEAzkUhaCQ1C3nD/XtiSyfiBVirnhNiKbmwQdGMTq9Pan3b3T/IqQZH1NjksjohfEv9IHO6oWQVsQWRDNW5rZVLOGCImrNXT3emY+TWK4Ey8SUibmgubT5P8zbgzTfEdvbX3Xd0FzbaRcVH/UVlv1E0sEYFsxHLvNK0URygeCE3gmDE7r1+kGqN6a4oC6QnGBemmVy9M4zTJ5O1wdSDkZd6cVeeKUPmcE2Ji2NX6ZOkB1OVz+aAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="802" height="677"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/28.6e008e4.802.png" srcset="/assets/ideal-img/28.6e008e4.802.png 802w" width="802" height="677"></noscript></div></div><p>정상적으로 연결되어 DataGrip에서 DB list를 출력하고 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAANCAYAAACQN/8FAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA/UlEQVQokX2RS0vDQBSF83PElQslj5nkzjuThGJDixVxqSD+//0nSbGlVl18HLicmXPnTOFCxvQJlzI+ZYwLiPOIC9iQcLFftYg5k8YJHxIiFvER3w8Yn5A+sdnteZz3FKVN3LmRLs+kNuIax21luXkQ7pUgxlDVDUXTWSRkQj8Ql3gfScNEnjZrpA09tWopVGdYaFpZEWcZphHjDKrVKK0oq/Jo1GJP+M0r0+GTOL8Ttm+47QdN2J2N3yppxgxP2PEZMxww4wu1Ga+NuutotD6h2/Yy+ucK62xR4yhr9bvxcvaP8eLAXzceqzpWtiKWcin86tXrX58xPlIrzRcX++RY4/jEHgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="458" height="620"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/29.e3481c1.458.png" srcset="/assets/ideal-img/29.e3481c1.458.png 458w" width="458" height="620"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAnklEQVQImW3NS3KFIABEUZYTQAQjyBMUBd8n+9/QTckogwxO9aC7qsXr9cN5vWm1kfJGXDNxTV2ID1r70K4P4mpPylExZkRJiVLqD4lzE9M0I3I5yffQ2n+GCim/0Foj9qOyHSfWOfQwMBiDNqbn/WKM6Z3I50XcK0sq7GkjP1bWJeJnz/Q9d2GJiFKfpFK76AOzdXjncNZixtuID4Fff0VgqHSsAigAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1010"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/30.f04382f.1680.png" srcset="/assets/ideal-img/30.f04382f.1680.png 1680w" width="1680" height="1010"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="결론">결론<a class="hash-link" href="#결론" title="제목으로 바로 가기">​</a></h2><p>사실 Server쪽이 아니면 크게 설치시 어려움은 없다.</p><p>하지만 대부분의 튜토리얼이 MongoDB 설치 후 <code>mongod</code> 으로 server를 활성화 시켜서 설명을 진행하는데</p><p>사실은 백단에서는 Windows Service으로 <code>mongod</code> 가 실행 중 이여서 <code>C:\data\db</code> 와 같이 지정한 경로에 DB가 저장되지 않을 수도 있고</p><p><code>mongod</code> 를 종료해도 <code>mongo</code> 으로 계속 Local Server에 접속되는걸 보고 <strong>뭐야 서버 왜 안죽어!</strong> 를 경험할 수도 있다.</p><blockquote><p>필자도 위의 이유로 화가나서 작성되었다.</p></blockquote><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="reference">Reference<a class="hash-link" href="#reference" title="제목으로 바로 가기">​</a></h2><ol><li><a href="https://blog.rhostem.com/posts/2018-08-31-mongodb-connection" target="_blank" rel="noopener noreferrer">친절한 설명</a></li></ol>]]></content>
        <category label="mongodb" term="mongodb"/>
        <category label="datagrip" term="datagrip"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[dotenv 파일의 환경 변수는 왜 사용하는 걸까?]]></title>
        <id>/2020/10/10/why-env-var-in-dotenv-files-used</id>
        <link href="https://parkgang.github.io/blog/2020/10/10/why-env-var-in-dotenv-files-used"/>
        <updated>2020-10-10T03:00:00.000Z</updated>
        <summary type="html"><![CDATA[연결정보는 결국 텍스트 혹은 숫자의 형태로 되어있을 텐데 .json, Object 등 사용하지 않고 왜 굳이 .env 를 사용해 관리하는 걸까?]]></summary>
        <content type="html"><![CDATA[<p>연결정보는 결국 텍스트 혹은 숫자의 형태로 되어있을 텐데 <code>.json</code>, <code>Object</code> 등 사용하지 않고 왜 굳이 <code>.env</code> 를 사용해 관리하는 걸까?</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="왜-굳이-env">왜 굳이 .env?<a class="hash-link" href="#왜-굳이-env" title="제목으로 바로 가기">​</a></h2><p>이에 대해서는 장점과 단점이 숨겨져 있다.</p><p><code>express</code> 를 사용한다는 가정으로 <code>.json</code> vs <code>.env</code> 을 비교해보겠다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="json-단점"><code>.json</code> 단점<a class="hash-link" href="#json-단점" title="제목으로 바로 가기">​</a></h3><ol><li>파싱작업이 들어가야함</li><li>express에서 set과 get 형식으로 작업해야함<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAMCAYAAABbayygAAAACXBIWXMAAAsTAAALEwEAmpwYAAABHUlEQVQYlUWR247kIAxE8//fOdOTEAhXGxIgZ7cz6t2SLL9YOq6q5bSOdTN8vwyHzcSs+CTMefPWfd/PLD1lnM9YG4hJCKWxeyUkpffOR8vIhRgCzgVyOClFsT6yu0AqSpSO1M5SSqEUodWLKhcijVOUS5Va6/PCg5ai5CykXJBy0trJGJM+5rPfRw+6ZCUnIaWElo6WE9GLXDtFT67r98/Ff60EEzA/Br877OvFunoOm7hb466Ve04W99rxXngZh3ER6w7W3bP5xp5ONAvMwRJWS4rK7jzuiHh/YF3EhBOfK6NWGIPFf2/ED9oc+G1jXx2bSWSf/qNrezu9qO2i6eDqA6kN0Uof47edtxmV+riNpTHn/NfER088f3P8A1B9z/j7wi6GAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="353" height="417"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.6ea00f3.353.png" srcset="/assets/ideal-img/1.6ea00f3.353.png 353w" width="353" height="417"></noscript></div></div></li><li>모듈이라 스코프가 동일하지 않아서 해야 하는 작업임</li><li>데이터가 추가될 때마다 set 해줘야함</li><li>함수에서 쓸려면 req 파라미터 넘겨줘서 <code>req.app.get</code> 패턴으로 사용해야 하며 <code>req</code> 파라미터 없는 함수에선 <code>require</code> 를 해야 함<div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAj0lEQVQImR3KW46EIBBAUfa/yIkzmY5gUUQF5Km3032+j1HvCW5HXeS0DreuvKyn5Eq5ClcujFoxjEHTgBNBrGVd/tFfiy4v5OeP5ISeMuazy36wiUdEKMfJTIl0Hly1Usukt4nJKRE1fJOGgGwbKh6VnRg7OTbGGBiAIxUkZu570lqj9c7oD6PdjD55noc3PjK/rSDcEJgAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="440" height="194"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.3581f26.440.png" srcset="/assets/ideal-img/2.3581f26.440.png 440w" width="440" height="194"></noscript></div></div></li></ol><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="env-장점"><code>.env</code> 장점<a class="hash-link" href="#env-장점" title="제목으로 바로 가기">​</a></h3><ol><li>배포 시 Shell으로 변경이 가능 하여 수정이 용이하고 ShellScript를 이용해 <code>SSH</code> , <code>CI/CD</code> 에서 활약을 기대할 수 있다.</li><li><code>express</code> 에서는 <code>NODE_ENV</code> 는 환경변수를 통해 개발환경과 운영환경에 따른 설정정보를 다르게 가져올 수 있다.</li></ol><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="이외-비밀-설정-정보-관리">이외 비밀 설정 정보 관리<a class="hash-link" href="#이외-비밀-설정-정보-관리" title="제목으로 바로 가기">​</a></h3><p>DB connection, API Key 등 SCM(source code management)으로 공유되면 비밀 정보가 노출되므로 별도의 저장이 필요한데 이럴 때 사용할 수 있다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="관리-방법은">관리 방법은?<a class="hash-link" href="#관리-방법은" title="제목으로 바로 가기">​</a></h2><p>매우 간단하다 연결정보가 포함 된 파일을 분리하고 해당 파일을 <code>.gitignore</code> 을 통해 버전관리를 하지 않도록 하면 된다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="적용-방법">적용 방법<a class="hash-link" href="#적용-방법" title="제목으로 바로 가기">​</a></h2><p>디렉토리를 생성한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAxElEQVQYlW2Q207DMBBE/ReUuHZS0thxHLu5QEOvUgUIiQf+/28OShRSCfEwqx3pjLSzQmqP9Uf0JiJ1zegTVS2Ssxcq22HcmW15oHAnVBZIlJ9D9RzwiHGE9ka3/6QfvhiO34TmRhWv+HjFhQtVuCBW0mLjmd3zG93LB03/jqtPmOoVO+mAcQMiWTt0sUfnHSr1PCSWlSx5XLu7ZIkYl43pSfN2Of6vpjITWHSkeYPK4lzmn9Yj+GR6sm27gDK9v+kX/AGQkHO79NdwbQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="264" height="223"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.3441cc4.264.png" srcset="/assets/ideal-img/3.3441cc4.264.png 264w" width="264" height="223"></noscript></div></div><p>해당 디렉토리로 이동 후 <code>npm init -y</code> 을 해주도록 한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAMUlEQVQImQXBQQ4AEAwAQU+RuAnVChc0/P9TayZkWei4SHeqOTYfVQ+lbYpuxJyYlA877Q/fOfvHQQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="605" height="55"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.ece025f.605.png" srcset="/assets/ideal-img/4.ece025f.605.png 605w" width="605" height="55"></noscript></div></div><p><code>dotenv</code> 과 <code>express</code> 를 설치하도록 한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAK0lEQVQImWMwNHX4b2Ht9t/E3Om/ubXbf1MLZzDb2Nzpv5mlC5jWM7T9DwBMzRC/6UU8AgAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="694" height="21"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.f9ed8be.694.png" srcset="/assets/ideal-img/5.f9ed8be.694.png 694w" width="694" height="21"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAMUlEQVQImQXBWQ4AEAxAQVfygdImJLVE3P88z0yIaaD9Uu1QdGHjkdskV6foRuyQxPlTrxEjqvMdPwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="613" height="19"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.8dada03.613.png" srcset="/assets/ideal-img/6.8dada03.613.png 613w" width="613" height="19"></noscript></div></div><p>프로젝트 루트 경로에 <code>.env</code> 와 <code> .env.dev</code> 를 생성한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAj0lEQVQImU2NSwrCMAAFc4lqP2mSNo2VpkmrFV24U3Dl/a8zkojgYhgePBhRyZFGHSnrgUo6qsZR7HuK0mbvSpsRrQm4aUV1Hm0DZgi0ZqZuJxqV8EjtEcoElhi43d+cry+W7cF6eWb/I6QJaOux40Z/OGGGhc6t6D6SaqqLqD5+j8qm7IxxMacb7XP6R9ofUVtIBi6MTnUAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="238" height="115"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.2d3f1d3.238.png" srcset="/assets/ideal-img/7.2d3f1d3.238.png 238w" width="238" height="115"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAn0lEQVQImU2OWw7CIBBF2URthTJQKKbhUapfmmiMLqD73801Q3/8ODkhuXOCkHqBogWDnHEeAwYV0PUep+Gg6x0URQjjV4S4YQob3OWApoLRFGjLZFh/hSCbUdeK+3PH47Wj3j5Y/6i3L/L2huBL6xLsXKFbKbcKWxE7wfgNgvPk+VFg5tJMjkcRUsdmcpWLXEigKYPc8SceSJ2giDmGPx0OVjRJJbDnAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="246" height="139"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.46b19f3.246.png" srcset="/assets/ideal-img/8.46b19f3.246.png 246w" width="246" height="139"></noscript></div></div><p>데이터를 작성한다.</p><div class="language-env codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">.env</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-env codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">id = 리얼환경</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">password = hi</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="language-env codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">.env.dev</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-env codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">id = 개발환경</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">password = hello</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAALklEQVQImWPQM3L6r6Fj819V0/K/mqblf3Vtm/+Gpi7/tfVsIWJaVv81de3+AwA1PA+r0wjBGAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1378" height="115"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9.2fea38f.1378.png" srcset="/assets/ideal-img/9.2fea38f.1378.png 1378w" width="1378" height="115"></noscript></div></div><p><code>app.js</code> 를 생성하고 아래의 코드를 작성합니다.</p><div class="language-js codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">app.js</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-js codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> express </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"express"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> dotenv </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"dotenv"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> path </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"path"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> app </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">express</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">dotenv</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">config</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">path</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> path</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">resolve</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">cwd</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">NODE_ENV</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"production"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">".env"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">".env.dev"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">req</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> res</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  res</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">send</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">아이디: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">process</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">env</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">id</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">\n비밀번호: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">process</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">env</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">password</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">listen</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">3000</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">NODE_ENV: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">process</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">env</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation constant" style="color:#36acaa">NODE_ENV</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Express Server Start"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAWklEQVQImV3MTQqAIBRGUXcSopX/9lAiqMxBk9r/fr6oQVCDM7pwWSMJostI0wlLFcqtH9oXmFDBeBvAZQKNB+xQYeL2xD8mugQuM2Le4alChwLlFvR2ft3nCxtaLt/cKYUXAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1631" height="505"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/10.129d6d7.1631.png" srcset="/assets/ideal-img/10.129d6d7.1631.png 1631w" width="1631" height="505"></noscript></div></div><p><code>node app</code> 으로 실행 해보면 <code>process.env.NODE_ENV</code> 은 <code>undefined</code> 으로 출력된다. 이 때문에 <code>.env.dev</code> 의 값을 끌어오고 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAK0lEQVQImWOwMXf6b2/l+t/I0Oa/kprRfzVN0//Kasb/ldWNwbSKhgmYBgA5TA94T5gGVwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="581" height="56"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/11.25ec14c.581.png" srcset="/assets/ideal-img/11.25ec14c.581.png 581w" width="581" height="56"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAb0lEQVQImVXF0QrCIABAUf//z6LoZS1Rox7cBmYz3JoL0t2oNy8crriFgFsTdp7Qd4fxHj307OWZXdtykJKjVogZmIDfFyACoWTG/KmIwXseMeLCyNVanq+ZdymsOVfEqWnQStF3HRdjSCnxb9sqXy9el8xsHIIfAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="265" height="112"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/12.bf6adb3.265.png" srcset="/assets/ideal-img/12.bf6adb3.265.png 265w" width="265" height="112"></noscript></div></div><p>환경 변수 값을 바꾸기 위해서는 해당 프로젝트 경로에서 <code>set NODE_ENV=production</code> 을 지정해주면 된다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA2ElEQVQYlWWQwW6DMBBE/RttIEAwDtjAGjtOQquAAAWVQ///c6ayEace5jRvZ2eX8bLHZyzBrzfc+w3a/YDcgqp5oax3Vc0AVkiHKFFILh262wb7/EVrJ8h2CMAxwIQkxKlEmnfo3Ap930BmAdkZSo9QNKKsB7BS9TgldQBbu0K7N1qzoDEenKBo2lcL9UB0bpBxA7IrzGMFuRm1ngNw9GRxpnE6K6TcBOOqvHH08xr3RH/xR1SFY4T8hpBfKKr+n5hP83CU1MiFDW/KeIc0J3BBKAqN/EL4A24Gewl/8p67AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="960" height="752"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/13.b7eb42f.960.png" srcset="/assets/ideal-img/13.b7eb42f.960.png 960w" width="960" height="752"></noscript></div></div><p>서버를 재실행하니 환경변수 <code>NODE_ENV</code> 가 변경되고 지정된 <code>.env</code> 를 불러오는걸 확인할 수 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAABCAYAAADn9T9+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAKUlEQVQImWMwsXD+b2Pp8t/IwPq/kqrRf1VN0//KakZQbPxfRd0EzAYANwAPZO9vOEwAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="580" height="58"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/14.a898723.580.png" srcset="/assets/ideal-img/14.a898723.580.png 580w" width="580" height="58"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAf0lEQVQImS3F0QqCMBiA0b3/k9mFXqRGCBaYWZnTik03df8XSReHo0695ukd56Hn+LhT6o59WRJlKVGasstSkqJA6bAySNi8ELp1ofWOmzU01nAfLa2bUL01vJ1DG0OjNR/vmST8yfYYAiqJYw55zqWquNY1yzyDgISABIEfEb7/jJeLv5uIVQAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="268" height="109"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/15.5d29c5b.268.png" srcset="/assets/ideal-img/15.5d29c5b.268.png 268w" width="268" height="109"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="gitignore-에-추가"><code>.gitignore</code> 에 추가<a class="hash-link" href="#gitignore-에-추가" title="제목으로 바로 가기">​</a></h3><p><code>.env</code> 은 암호화된 파일이 아닙니다! 이 때문에 조회/수정이 모두 가능합니다.</p><p><code>.gitignore</code> 으로 해당 파일은 버전 관리 되지 않도록 설정합니다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="config-디렉터리">config 디렉터리<a class="hash-link" href="#config-디렉터리" title="제목으로 바로 가기">​</a></h2><p>관심사의 분리(Separation of Concerns) 원칙을 적용하여 설정 정보와 코드는 분리되는것이 좋다.</p><p>제품 전략에 따라 다르지만 설정에 대한 정보를 하나의 디렉터리로 모으는게 어떨까? 아마 팀원을 위해 좋은 선택일 것이다.</p><p>이 때문에 <code>config</code> 디렉터리에 설정 파일을 모두 밀어넣도록 해보겠다.</p><p><code>.env</code> 파일의 기본 위치는 프로젝트의 루트 디렉토리이다.</p><p>하지만 <code>dotenv.config();</code> 에 path를 지정하면 다른 위치에 있거나 기본 이름이 다른 <code>.env</code> 파일을 참조할 수도 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAqElEQVQImVXNy27CMBSEYT9GFWx8nDgB45y0uW2CyAKJHbz/4/woEWrVxWg2n2bM+edGMz8IaSHmlWM1UbgLh2Pe20qHFcUU9USz3EnjgpwGvorTP+iC4krFSPxmmHuaNCBVj1QjTrodFS5jfcZKxqRWeb6uSHMj1DMhTjjRD7xgvWJ9hyljZhiVKq2EesKXPRLHHfxBxbiyJep2feXcrntv99a3v6tb3s3hV0oFYJYBAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="237" height="159"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/16.13cfd70.237.png" srcset="/assets/ideal-img/16.13cfd70.237.png 237w" width="237" height="159"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAaUlEQVQImT3H3QqCMACA0T1Nqc1g8ze2ubVMULHAvOii93+NLxDq4lwcUZsHxr8oLwuFe1P6D911RavAUTrSs9+JXEVs2KjMk5O+0fg7YZ6I80gbeuwwInWPaOoBVUUOmSWR7i/Nu93vXyTWMjIJToe5AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="197" height="62"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/17.5695862.197.png" srcset="/assets/ideal-img/17.5695862.197.png 197w" width="197" height="62"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="reference">Reference<a class="hash-link" href="#reference" title="제목으로 바로 가기">​</a></h2><ol><li><a href="https://wedul.site/479" target="_blank" rel="noopener noreferrer">개발과 운영 환경 구분하기</a></li><li><a href="https://m.blog.naver.com/PostView.nhn?blogId=couponpapa&amp;logNo=221233091940&amp;proxyReferer=https:%2F%2Fwww.google.com%2F" target="_blank" rel="noopener noreferrer">논리적인 설명</a></li><li><a href="https://brunch.co.kr/@topherlee/73" target="_blank" rel="noopener noreferrer">실제 만드는 사진이 있음</a></li><li><a href="https://hudi.kr/node-js-dotenv-%ED%99%98%EA%B2%BD-%EB%B3%80%EC%88%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0/" target="_blank" rel="noopener noreferrer">연결 예제 코드</a></li></ol>]]></content>
        <category label="dotenv" term="dotenv"/>
        <category label="환경 변수" term="환경 변수"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[JSP Project에 CI/CD를 할 수 있도록 설정하기 (feat. JSP + Gradle Project 만들기)]]></title>
        <id>/2020/09/27/setting-jsp-project-to-do-ci-cd</id>
        <link href="https://parkgang.github.io/blog/2020/09/27/setting-jsp-project-to-do-ci-cd"/>
        <updated>2020-09-27T03:00:00.000Z</updated>
        <summary type="html"><![CDATA[CI/CD 가 무엇인지 아는데 불구하고 JSP 를 이용해 제작된 웹 애플리케이션 제품을 만들면서 수동으로 배포하는 것에 대해 아쉬움을 느낄 때가 많았다. 그동안 개발부터 배포까지 Full Workload를 자동화 해보고 싶었는데 이번 기회에 제작을 시도해보려고 한다.]]></summary>
        <content type="html"><![CDATA[<p><code>CI/CD</code> 가 무엇인지 아는데 불구하고 <code>JSP</code> 를 이용해 제작된 웹 애플리케이션 제품을 만들면서 수동으로 배포하는 것에 대해 아쉬움을 느낄 때가 많았다. 그동안 개발부터 배포까지 <strong>Full Workload</strong>를 자동화 해보고 싶었는데 이번 기회에 제작을 시도해보려고 한다.</p><p>처음이라 그런가 2 ~ 3주 정도 삽질을 하게 되었었으며 열심히 삽질한 결과를 자세히 리포팅 해보도록 하겠다.</p><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><ol><li>해당 글에서 <code>CI/CD</code> 가 무엇인지 다루지 않습니다.</li><li><code>IntellJ IDEA</code> IDE를 이용하여 프로젝트를 설정합니다.</li></ol></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="근데-cicd-에서-어떻게-build-하지">근데 <code>CI/CD</code> 에서 어떻게 Build 하지?<a class="hash-link" href="#근데-cicd-에서-어떻게-build-하지" title="제목으로 바로 가기">​</a></h2><p>사실 <code>CI/CD</code> 는 별거 없다 지속적으로 통합/배포 하기를 위해서 project source code를 가져오고 build 하고 이후 추가 적업을 끼워 넣어서 진행하면 된다.</p><blockquote><p>사실 <code>Pipelines</code> 아라고 말하는 게 더 명확할 수 있다.</p></blockquote><p>근데 문제는... <code>CI/CD</code> 환경에서 Build 작업을 진행하기 위해서는 <code>CLI</code> 으로 Buuld를 할 수 있어야 하는데 이 부분을 어떻게 할지 몰랐다.</p><blockquote><p><code>CI/CD</code> 를 위해서 가장 첫 번째 관문은 IDE 외에서 빌드가 수행돼야 하는 점이었던 것이다.</p></blockquote><p>이 부분에서 가장 많이 시간을 소비하게 되었는데 살면서 IDE에 의존해서 빌드와 Release를 진행했었지 CLI으로 빌드 해본 적이 없었기 때문이다.</p><p>이 부분에 대해서 많은 리처시가 필요했는데 결론적으로는 <strong>jsp 프로젝트에 Gradle을 사용</strong>하는 것이였다.</p><p>찾으면서 알게된 것으로 JSP의 Framework인 <code>spring</code> , <code>spring boot</code> 은 기본적으로 Gradle이 붙어있다고 한다.</p><p>요즘 시대에 동적인 웹 페이지를 위해 jsp 생짜로 개발하는 개발자는 많지 않을 것이다. 이 때문에 Framework를 이용하지 않고 jsp 자체에 Gradle를 사용하는 Docs와 레퍼런스가 굉장히 적었다.</p><blockquote><p>더구나 JSP가 오래된 기술이라서 이런 기술에 <code>CI/CD</code> 를 구성하는 글도 찾기가 어려웠다.</p></blockquote><p>그래서 해당 글에서는 <code>IntellJ IDEA</code> 으로 구성된 JSP Project에 <code>Gradle</code> 를 부착하여 Build 하는 것이 주된 내용이 될 것이다.</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="empty-project-생성">Empty Project 생성<a class="hash-link" href="#empty-project-생성" title="제목으로 바로 가기">​</a></h2><p>모듈을 포함한 Project 생성시 모듈 디렉토리가 생성되지 않기 때문에 Empty Project부터 생성합니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAeElEQVQImXXN2woCMQyE4T6X2yZpspW69cZLUfH9n+KXFTzBevExkGFI8j4Q32Pjgo4bulw3pXY4Yj6TLci25pYgzX2hqDOJMon9lebeEXsd6qfUSv6S2ulOjDPFAqmOmCM1nuVU1i/Krigpeydb+1m/xYLGHovGA0U3ajaQB6jXAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="993" height="606"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/1.da219cb.993.png" srcset="/assets/ideal-img/1.da219cb.993.png 993w" width="993" height="606"></noscript></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAW0lEQVQImYXMSQqAMBBE0dwrQw+Z0Kj3v02JQogQ0cWDWhTfSN0Qlx1xOXBtKuuEa4NxHBGkU3h+ZywpbOBf99ERw5F8GkWSR6FvGsWgGX4qMEgTQm6gWCAp4wRygGjkL8nq1gAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="993" height="606"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/2.2209dd1.993.png" srcset="/assets/ideal-img/2.2209dd1.993.png 993w" width="993" height="606"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="모듈-생성">모듈 생성<a class="hash-link" href="#모듈-생성" title="제목으로 바로 가기">​</a></h2><p>왼쪽에서 Gradle를 클릭 후 반드시 <strong>Web</strong>으로 생성해야한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAfklEQVQImW3MSw7CMAyEYZ+LpIkdx+TRgmCFEHD/YwxKKyREWXybkeanVDomSWCr4FzgWfdEQTZfIdaRawMnhQ8R7g86Xl7gdkNdzhBrcHFU8g8F5VoQJCGfHpD5iVDviG2PtC1gNRwmhgsCHzduCGPb0Mh+Dx/rQTui2hp6AxNnapK9xqIFAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="993" height="606"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/3.c9c634f.993.png" srcset="/assets/ideal-img/3.c9c634f.993.png 993w" width="993" height="606"></noscript></div></div><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p><code>Java</code> 모듈로 선택 후 나중에 <code>프레임워크 지원 추가</code> 로 <code>웹 서비스</code> 를 추가하는 방법은 Gradle에서 Build 된 <code>.war</code> 이 정상적으로 동작하지 않는다. 해결 방법은 추가적인 R&amp;D가 필요해 보임.</p></div></div><div class="admonition admonition-caution alert alert--warning"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>caution</h5></div><div class="admonition-content"><p>Empty Project에 모듈을 추가하는 거므로 저장 경로를 주의하기 바란다!</p></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAbElEQVQImYWOWwpCMQxEu6/2NmkeTa5c3P9uRqp+WBH8OEwGzkAKWUDjhHiCdP4g0HWidHHkdUfeLshMsAeG5wZboDRWDHOwOkgMJPqR73soSiVB7fyXp9iI0Whs1L74FlfZpNfwYMN67WDBA9bpaWIaPTOmAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1166" height="774"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/4.ded441c.1166.png" srcset="/assets/ideal-img/4.ded441c.1166.png 1166w" width="1166" height="774"></noscript></div></div><p>설정의 속성의 다음과 같습니다.</p><table><thead><tr><th align="center">속성</th><th align="left">설명</th></tr></thead><tbody><tr><td align="center">GroupId</td><td align="left">Gradle를 고유하게 식별하는 값이다. 주로 도메인 주소를 거꾸로 적는 것으로 알고 있다.</td></tr><tr><td align="center">ArtifactId</td><td align="left">Build되는 아티펙트 이름이다. 주로 module의 이름이며 IntellJ에서 알아서 module와 같은 이름으로 지정해 준다. 나중에 Gradle Script으로 변경가능</td></tr><tr><td align="center">버전</td><td align="left">아티펙트 뒤에 <code>-</code> + <code>0.1</code>으로 조립되는 값이다. 나중에 Gradle Script으로 변경가능</td></tr></tbody></table><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="하이어라키-구조">하이어라키 구조<a class="hash-link" href="#하이어라키-구조" title="제목으로 바로 가기">​</a></h2><p>생성된 하이어라키 창이다. 중요한 부분만 짚도록 하겠다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAQCAYAAAAvf+5AAAAACXBIWXMAAAsTAAALEwEAmpwYAAABZElEQVQokVWQy27bMBRE9VuNZUuyJJLiMxT1CoLYbgt003X3ab3oZ59CjmPXi4MLDAYzuJM16UiX3mjcRKkHtjKyaQOtX9ibkdbNlF0iM87yMvVM00CfIsZ0qE7SdfJ+lSCzpzM/vv3i68+/mOMZ/faOOfzGHP9gV05nzOGdzDtF7wSHOTBNPWPyTENg6C0xaIKTeCvIjCrpdcFhUKSoeXYN0QuCrfG6wqgCIwuyXbknLa/4EMh3BZsbJXmxUl1utgqNMnRpphbqaqjYrOw+KclW8Snf0cQJHRNFVV9Tqlvi3bgtkNrQjyPGhf+M1d34lBdsy5paWZyUiM5cUld9sy0vrEFZ4wQiaOL3Gb94GitonXxg1TIRJCoa+hfPcprxc2TV7ihaLz+qlXvGxwGh9MeX18qH6i/5Du0jcZzR1lHWzW2ih2fKVtCFHuvX1ESr9MPQN+NeO1Ra0MahrENq+zD8p/EfQP4njjye7DAAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="294" height="482"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/5.9dedd7c.294.png" srcset="/assets/ideal-img/5.9dedd7c.294.png 294w" width="294" height="482"></noscript></div></div><table><thead><tr><th align="center">디렉토리</th><th align="left">내용</th></tr></thead><tbody><tr><td align="center">build.libs</td><td align="left">Gradle에서 Build 파일이 저장되는 위치</td></tr><tr><td align="center">main.java</td><td align="left">jsp의 servlet</td></tr><tr><td align="center">main.webapp</td><td align="left">jsp web이다 jsp 파일이 들어간다</td></tr><tr><td align="center">test.java</td><td align="left">unit test에 필요한 코드를</td></tr><tr><td align="center">build.gradle</td><td align="left"><strong>Gradle의 Build 및 Task와 라이브러리 종속성 추가를 위한 Script를 작성하는 곳</strong></td></tr><tr><td align="center">gradlew</td><td align="left">유닉스용 실행 스크립트</td></tr><tr><td align="center">gradlew.bat</td><td align="left"><strong>원도우용 실행 배치 스크립트</strong></td></tr><tr><td align="center">settings.gradle</td><td align="left">Gradle이 사용되는 rootProject를 지정하는 곳</td></tr></tbody></table><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p><code>gradlew</code> or <code>gradlew.bat</code> 가 있기 때문에 로컬 PC마다 Gradle이 install 되어있지 않아도 build.gradle가 실행 가능한 것이다.</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="실행-환경-구성">실행 환경 구성<a class="hash-link" href="#실행-환경-구성" title="제목으로 바로 가기">​</a></h2><p>빌드 자동화 툴인 Gradle을 사용하기 때문에 더 이상 IntellJ의 아티팩트를 Tomcat에 배치하지 않아도 된다.</p><p><strong>앞으로 Gradle에서 build <code>.war</code> 파일을 사용할 것이다.</strong></p><p>build된 파일의 경로를 알기 위해서 1회 buiild를 진행하도록 하겠다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="1회-build-실행">1회 build 실행<a class="hash-link" href="#1회-build-실행" title="제목으로 바로 가기">​</a></h3><p>Gradle의 build를 실행하니 <code>build\libs</code> 경로에 <code>.war</code> 파일이 생성되는 것을 확인할 수 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAq0lEQVQImSWMwXKDIAAF/R1kgFCjokFAsaM2jdNb//9LNiM57OXN7qtevz/8nS9iTPS9o+t6hmFkzjvLfjLlnbgeVPuxsa2Z9mYxQhS0EKzLznH+4/OTsD6p0nbg4ozShlrKgqhrfEz4OPOYAj5EqpS/8WHG3CxKa7Q2KKVwo/+I4RMUMaSlDMPoabuuRBdXJKXEfjVUU1q4CHMuL/fOYZsWY+8Y26CNoXeON61HZGdGWiWzAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1010"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/6.660d557.1680.png" srcset="/assets/ideal-img/6.660d557.1680.png 1680w" width="1680" height="1010"></noscript></div></div><div class="admonition admonition-tip alert alert--success"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</h5></div><div class="admonition-content"><p>옆에서 클릭된 Tasks들은 하나의 명령어 들이다 즉, CLI를 이용해 실행이 가능하다.<br>
<!-- -->IDE에서 클릭하면 IDE가 해주는거 같아 보이지만 백단에서는 모두 shell기반으로 실행되고 있다.</p><details class="details_rxUF alert alert--info details_PYSW" data-collapsed="true"><summary>CLI으로 실행 방법이 궁금하면?</summary><div><div class="collapsibleContent_oXwz"><blockquote>PowerShell으로는 실행되지 않았습니다.</blockquote><ol><li>해당 프로젝트의 <code>gradlew</code> 이 있는 위치로 이동한다.</li><li><code>gradlew [원하는 Tasks 입력]</code> 을 실행한다.</li><li>실행 결과 화면<!-- --> <div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAwElEQVQImVXG206DQAAA0f0tDUmthVZhawttQK6hXJZLEIpiiYkx8cvHh+qDDyczolHfdMUXKprJvDPBocd1ejzn2j9it67ZGwprkfKoxRg3Iat/AvTbEKH8DxL7TH4YCM2ao1HgbRTeg8I3K9zfF8PzxOC/MwYzb+4rL05Pve+onlrUtkHJmlLWiPHYMdktn07DZVfRmCXztuQic2Z5YrJyRpkhrEVGcRcRLhM2WoytxVT3CekyotVjQr2gWqf8AFM6e9KoxQ4sAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1680" height="1010"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/7.42d2103.1680.png" srcset="/assets/ideal-img/7.42d2103.1680.png 1680w" width="1680" height="1010"></noscript></div></div></li></ol></div></div></details></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="일반-jsp-빌드-프로세스">일반 jsp 빌드 프로세스<a class="hash-link" href="#일반-jsp-빌드-프로세스" title="제목으로 바로 가기">​</a></h3><p>일반 적인 jsp 프로젝트에서 빌드를 하고 배포한다면 아래와 같은 프로세스로 진행된다.</p><ol><li><code>Tomcat 9.0.37</code> 의 실행/디버그 구성이 실행됨</li><li><code>실행 전</code> 에서 단계에서 아티팩트가 빌드됨<ul><li>IDE 자체 방식으로 <code>.war</code> 파일이 생성됨</li></ul></li><li><code>Deploy at the server startip</code> 의 파일이 Server으로 배포됨<ul><li>build된 <code>.war</code> 파일이 애플리케이션 컨텍스트 경로로 Tomcat에 배포됨</li></ul></li></ol><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAj0lEQVQYlX2QSQpCMRBEcyc3YpIe0hnAjwi68P4nKen4UaLg4lFQPKqhQ2obTjpwiAqyAe1nSB1gG8ilIZcOso6QtCJywTExdFxh2wM87hPqN5BnvSC4lEiQWBFJED29++ItTukPP6IvZ/EVXfiIrBBrUGuQ2kBqC4tI5VUua/uVkMTeZ7PLuzg7qfM9XAxPmGCLzQyVDMUAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1078" height="870"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/8.ecdf69f.1078.png" srcset="/assets/ideal-img/8.ecdf69f.1078.png 1078w" width="1078" height="870"></noscript></div></div><p>우리는 여기서 Gradle으로 build 하도록 하고 <code>.war</code> 의 경로만 바꾸면 된다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="gradle-빌드-프로세스">Gradle 빌드 프로세스<a class="hash-link" href="#gradle-빌드-프로세스" title="제목으로 바로 가기">​</a></h3><p>새로운 실행/디버그 구성을 만들도록 하겠다. 구성 편집을 클릭한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAaklEQVQImR3KWQ6CMBRAUbZlKp1L+0rQRFOcwgY0xrD/j0vw+5yut5Z7PPOSxrM0bumCmxo+C9Z5QhZiETobEtP85rqsnB4/avuS5w8mCqo37J72aFzAx4iMlTpWhjLgvENpg9KWw1H/8wZsfzeYjnl+nAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="462" height="139"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/9.bdf3238.462.png" srcset="/assets/ideal-img/9.bdf3238.462.png 462w" width="462" height="139"></noscript></div></div><p>왼쪽 상단의 + 버튼을 클릭하여 Tomcat 로컬을 클릭한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAjUlEQVQImW2MQYoDMQwE/avA2GPJki3vLCSQS/L/l1SwWXLaQ1FNHypd9xfz8eYmg9MCnxfag2r9i3iQ/HpiP0+KdsQc64GNiY3lwGLtSSqilFopVTnVqOaIr8of1lEfpKxOlkaRRq76L0VsFduu7eKuL+TrfNb9p8Mmhw6ONsgWHC3ILSgWZL8o/XfvD9x2aZYD+dIiAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1078" height="684"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/10.8135d86.1078.png" srcset="/assets/ideal-img/10.8135d86.1078.png 1078w" width="1078" height="684"></noscript></div></div><p>배포탭을 클릭한뒤 하단의 + 클릭 후 Gradle 작업 실행을 클릭한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAArElEQVQYlV2PXQ7CMAyDeyMkJthom6Rpuz8NxBv3v4lRM4YQD59aWXbiOK4rBinoSVHrBCkjWAtiyh8KKGW4IBmek0FJEeUgIXJCYEXgBOf5V0wIX+OOGUXbxN1opj9auG2yic1ooqh1Of4HLBlsHY/VTdQC0v2I1tcTY5o3LI/XfoytIrFkC9wiwZOCtGLe7li2J1zMI65R0A0B597b2/Uep4vHlQqkzNBxxRuGWYqYhYE0HAAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1229" height="1012"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/11.08c32b4.1229.png" srcset="/assets/ideal-img/11.08c32b4.1229.png 1229w" width="1229" height="1012"></noscript></div></div><p>현재 사용하는 모듈을 선택한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA3klEQVQYlS2Py0oDQRRE56fM5NGv2337NRMiSkicja6CCK4Ev/9oRhe1KDhVRQ1jnAn1SGozXis7F1dtXOQgmZ1LGC8Md6OlkltHkhJyoSXlLRdUAg97g7GWwaZKKhUnCX9XUmLMLJcbz8s7x8uNNJ0ZzL2xNoyTFY65IGUinT/pyxd9+UaOrwwmZkQzBxfwUdE2rUEtSojCZn/4m3apkmvDieJF0KykUtDaiaUx2vh/JiiiBRcEO70g5w98e2I+PZL7zGhlXRtGF1czmsBWT+z7la10fG6YWNhawfyCP/XXj81FkVPOAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="350" height="282"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/12.ccb7d86.350.png" srcset="/assets/ideal-img/12.ccb7d86.350.png 350w" width="350" height="282"></noscript></div></div><p>우리는 Tasks중 build를 실행할 것이므로 작업에 build를 입력한 뒤 확인을 클릭한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAYAAAALpr0TAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA3klEQVQYlUWQS3LCQAwFfatQNsxfmh/EQCoVilNkl+N3auxQWWjXryW96a08CPWd2FZSv2KkcXDC7JWDTcxOOMbCNPtMSILkSpSM9R73N1o7UirWByarHS2VqAWflJDPaF8J2tDbA+kr5gWGpMwny3xy+HbH1ytLKCyhsjhhMZZp7B/GYVuMI2gm1YaMtbWjrWNDehllsw1wBMYZI7CFcsH4uIOSCz4KR+u3UMo7EHP5N446oihBlKOLuLIS+h2vbYNzP+/g6Gk8spwMoyrz+Y1//jCXL/rlzOX2gUvKLy+Toj/D/W0cAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="281" height="258"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/13.c58771a.281.png" srcset="/assets/ideal-img/13.c58771a.281.png 281w" width="281" height="258"></noscript></div></div><p>이제 build된 <code>.war</code> 경로를 지정해 줘야 한다. 오른쪽의 + 클릭 후 외부 소스를 클릭한다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAHCAYAAAAxrNxjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAe0lEQVQYlXWOSw7DIAxEOVUlAvgHpM2+97/KVCY/VCmLWYz9PJ5AfUPkhlcWaH9D2grShiKGLAaWiiIVIbEhkiIWQRFF5luJGKor6ueLsJAPZSiRa4Z370xI1+BZF+jGE9iObtP7vcKZ+AfesMD3R6JNiXXA52HWPrwzP+DbeX9sJBBFAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1182" height="901"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/14.685a835.1182.png" srcset="/assets/ideal-img/14.685a835.1182.png 1182w" width="1182" height="901"></noscript></div></div><p>이전에 build된 파일 경로를 선택한다.</p><blockquote><p><code>\build\libs\.war</code></p></blockquote><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>현재 스크린샷은 위에서 가이드한 ProjectName과 Module 이름이 다르게 되어있습니다.</p></div></div><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAALCAYAAABGbhwYAAAACXBIWXMAAAsTAAALEwEAmpwYAAABKklEQVQYlSWQTW/bMBBE9asKlOanKFGkJIqWHAFO0B6CFAh6jGMf+utfQfkwl93ZnYdp1FAIY8aFhOurIroNCNMitONke6SPNF2aOW8X0pyJ08y0FMq6kaZMHCeGaaGNM410AWnsMcjnDes7fkqNNA7ddmjXoqyjEbZDSE1aVqayEcfn125InLQ9JJSlqQxCaea5sJeVkgt/Pv9yuz8OlPpdO1+NHUIbnPW8bjs5RNKQ2K9vhDgexrYLz+jKI5ThPC5c85mTNPwQ8pjVXVVj8gd+f+AuN+J+59fvf6zXG+v1m/zyhX+5Y8snjXA9snamHHHZGPOK0g5tWpRpkcYf+2e0Nvghkebl4HJdj7TuqKXGnrShMX1C29pVvXaoymQ9prxjpldCmvAh8h/cKsksHxbx9wAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="426" height="484"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/15.78aca5a.426.png" srcset="/assets/ideal-img/15.78aca5a.426.png 426w" width="426" height="484"></noscript></div></div><p>최종 반영된 프로세스이다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAArklEQVQYlW2MW27DMAwEfakWTiWRlBgpTt2H3SD3P8oUsl0UAfIxGC6x5KBtJuTGSzSszvj0hbUZre+IXxHvnhhO6oySeQ2K1k/ydEPqgtR1d1sR/2AIWohqJM2EpAR5zlYMYv/0g23u/sOOj0cpWUGyo+VMymVDuq30ou9fNGPnhnlFvZKyPzBEUWJKG6KKWSaJHLtIjJ3EMF5Wxvq90xZOPbdlz5cbb9c7YfrhF55ejFYoidF1AAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="1078" height="885"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/16.09a3c46.1078.png" srcset="/assets/ideal-img/16.09a3c46.1078.png 1078w" width="1078" height="885"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="실행">실행<a class="hash-link" href="#실행" title="제목으로 바로 가기">​</a></h2><p>정상적으로 실행되는 것을 확인할 수 있다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAXElEQVQImX3FywqEIBiAUd//ddr0GG0bGKPIUSm7oRP+EV9Q+87mqHoKdOtCOA/8vuNSpHKW4lNTNppSf+/VJkL4J37LjBlHtDG01tJ5T+ue+2FASRZSjEjOvLkAxSBxU87c5ngAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="446" height="139"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/17.bdc8b04.446.png" srcset="/assets/ideal-img/17.bdc8b04.446.png 446w" width="446" height="139"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="web-inf-추가하기">WEB-INF 추가하기<a class="hash-link" href="#web-inf-추가하기" title="제목으로 바로 가기">​</a></h2><p>Gradle jsp Project는 <code>\WEB-INF\web.xml</code> 을 포함하고 있지 않기 때문에 알아서 만들어줘야 한다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="web-inf-생성">WEB-INF 생성<a class="hash-link" href="#web-inf-생성" title="제목으로 바로 가기">​</a></h3><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAZ0lEQVQImSWJAQqCQBAA7/0+QtCS/tAPCs8X9IEENaPM29vdia6BgYEJ1fnGKd6prxP1ZaTpZw5xoekn2jhzHB50w0KgkFmfayk3Q1JCRXC3/wbCe8+8tp1PymQDUSepFX+t5myifAHstHAk5wtNVwAAAABJRU5ErkJggg==&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="939" height="281"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/18.b7d35c9.939.png" srcset="/assets/ideal-img/18.b7d35c9.939.png 939w" width="939" height="281"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="webxml-생성">web.xml 생성<a class="hash-link" href="#webxml-생성" title="제목으로 바로 가기">​</a></h3><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAYklEQVQImVXGQQrCMBRF0ay/a3Bgiu0mXECh0e6g40IxxEEatPn5Vxpx4IXHO6a5zvRu4TSsnMcH9uZp7wHrfPVletJNAUMtE+P2pSpFBC1S/cukdyamF8fnArvo36QoaRc+73dwO5VGfEwAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="939" height="281"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/19.acc5f55.939.png" srcset="/assets/ideal-img/19.acc5f55.939.png 939w" width="939" height="281"></noscript></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="webxml-기본-스키마-작성">web.xml 기본 스키마 작성<a class="hash-link" href="#webxml-기본-스키마-작성" title="제목으로 바로 가기">​</a></h3><div class="language-xml codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_SpHG">web.xml</div><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-xml codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token prolog" style="color:#999988;font-style:italic">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">web-app</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">xmlns</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">http://xmlns.jcp.org/xml/ns/javaee</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">         </span><span class="token tag attr-name namespace" style="color:#00a4db;opacity:0.7">xmlns:</span><span class="token tag attr-name" style="color:#00a4db">xsi</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">http://www.w3.org/2001/XMLSchema-instance</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">         </span><span class="token tag attr-name namespace" style="color:#00a4db;opacity:0.7">xsi:</span><span class="token tag attr-name" style="color:#00a4db">schemaLocation</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">         </span><span class="token tag attr-name" style="color:#00a4db">version</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">4.0</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">web-app</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="gradle-dependencies-추가">Gradle dependencies 추가<a class="hash-link" href="#gradle-dependencies-추가" title="제목으로 바로 가기">​</a></h2><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAlUlEQVQImUWLSQ7CIAAAeY4sWlFbkdauBEQbl7v//8aYVBMPk7nMiNc9c82BFMfFY5jwvsZaS7H9Ix5z5D4nUhxIsed0cmyKAmM0UkrUD9HHTEhXmm6gbgcq57F2i9EKrRXGmGUSzZQ4T4m97yibgV15pB0zl+ebg6uXeL1ZI6Y804dE1XS49hs639KHG3ZXIuUKpTQfhoxTgshjegsAAAAASUVORK5CYII=&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="965" height="503"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/20.2833aaa.965.png" srcset="/assets/ideal-img/20.2833aaa.965.png 965w" width="965" height="503"></noscript></div></div><p>Gradle을 사용하기 때문에 일일이 프로젝트에 라이브러리를 연결하지 않아도 빌드툴이 알아서 라이브러리 종속성을 추가할 수 있다.</p><p>기본적으로 jsp build를 위해 필요한 library들이 있다.</p><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="javaxservletjavaxservlet-api401">javax.servlet:javax.servlet-api:4.0.1<a class="hash-link" href="#javaxservletjavaxservlet-api401" title="제목으로 바로 가기">​</a></h3><p>javax.servlet으로 컴파일 해서 war 파일을 생성한다는 의미입니다.</p><p>해당 라이브러리가 없으면 컴파일에 실패합니다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</h5></div><div class="admonition-content"><p>javax.servlet 으로 컴파일 해서 war 파일을 생성한다는 의미 입니다.</p></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="javaxjavaee-api801">javax:javaee-api:8.0.1<a class="hash-link" href="#javaxjavaee-api801" title="제목으로 바로 가기">​</a></h3><p>Java EE이다. 가장 많이 사용되는 부분은 jsp에서 <code>out</code> 함수 사용시 이용된다.</p><p>혹은 <code>compile group: 'javax', name: 'javaee-api', version:'7.0'</code> 도 가능하다.</p><div class="admonition admonition-info alert alert--info"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Java EE 추가 이유</h5></div><div class="admonition-content"><div class="language-java codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-java codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token class-name">PrintWriter</span><span class="token plain"> out </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> response</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getWriter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">out</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;/body&gt;&lt;/html&gt;"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><code>response.getWriter()</code> 는 javax에 포함되는 함수입니다.</p><p>Java EE를 라이브러리로 추가하지 않고 사용시 IDE상에서는 오류를 뱉지만 Tomcat내부에는 해당 함수가 있기 때문에 실제 동작에서는 문제가 없습니다.</p><p>하지만, IDE에서 제공하는 자동완성과 오류체크를 위해서는 해당 라이브러리를 IDE에 추가하는 방법이 옳습니다.</p></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="최종-gradle-dependencies-script">최종 Gradle dependencies script<a class="hash-link" href="#최종-gradle-dependencies-script" title="제목으로 바로 가기">​</a></h3><div class="language-gradle codeBlockContainer_nXGu theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_JJ8q"><pre tabindex="0" class="prism-code language-gradle codeBlock_lX7H thin-scrollbar"><code class="codeBlockLines_pw4m"><span class="token-line" style="color:#393A34"><span class="token plain">dependencies {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    testCompile group: 'junit', name: 'junit', version: '4.11'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    // servlet api</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    compile 'javax.servlet:javax.servlet-api:4.0.1'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    // java EE (jsp에서 out함수때 사용됨)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    compile 'javax:javaee-api:8.0.1'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup_rq_W"><button type="button" aria-label="클립보드에 코드 복사" title="복사" class="clean-btn"><span class="copyButtonIcons_gVBA" aria-hidden="true"><svg class="copyButtonIcon_Yh5B" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_vsWg" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="이외-주의할-것">이외 주의할 것<a class="hash-link" href="#이외-주의할-것" title="제목으로 바로 가기">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="ide가-기본적으로-스캐폴드로-잡아주는-스트럭처를-유지하고-싶다면">IDE가 기본적으로 스캐폴드로 잡아주는 스트럭처를 유지하고 싶다면<a class="hash-link" href="#ide가-기본적으로-스캐폴드로-잡아주는-스트럭처를-유지하고-싶다면" title="제목으로 바로 가기">​</a></h3><p>git에서 Empty Folder는 형상관리 되지 않습니다.</p><p>IDE가 기본적으로 스캐폴드로 잡아주는 스트럭처를 유지하고 싶다면 java 및 test 모듈, resource에 <code>*.txt</code> 와 같은 파일을 한 개 라도 포함하여 유지하도록 한다.</p><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>사실 굳이 추천하지 않는 이상한 가이드 입니다.<br>
<!-- -->IDE가 기본적으로 잡아주는 스트럭처를 다 이해하지 못해서 어떻게든 기본 구조를 가져고 싶은 경우에 눈물을 흘리며 해당 전략을 사용하도록 한다.</p></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="gradle-module-인식-못하는-경우">Gradle Module 인식 못하는 경우<a class="hash-link" href="#gradle-module-인식-못하는-경우" title="제목으로 바로 가기">​</a></h3><p><code>build.gradle</code> 파일에서 Gradle 프로젝트 가져오기를 클릭하면 됩니다.</p><div class="imgContainer_g3cu spacer_xInT"><div style="background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAQCAYAAAAvf+5AAAAACXBIWXMAAAsTAAALEwEAmpwYAAABSklEQVQokW2P2W7UQBRE/VeIwXbvi+1u7wlBIREviLcIif/XQd1kgmDyULqLjurWbfZj5TxXlnkkpUiaAjlFpte6LhPbmmjuVse3p5PPR2DPiiMr9qRqv2fFlmRVc5GSeZnJQ6ATPZ2WaGeQRtFriahSNEL3zNvIw/1OCAbtB+yQuQjDh1bysdcI42m22fH8fPL4sLImw5QmwphQxtL2EmksPgQaP82MeaWTiksnUNYzpBllHa1QdFLXXfOplxUotegKWh/plamw0JamNFcVUBpXT/9x9dgwYEP8C14d/ZCIU8b4+OZWT7/nGKeEtv4tY8l/A7o4smxHdSyzduH9jAW45muFrrv/wNevXSAMA9paeinphEAq/e8zpcbjO/nxF9OXn8T7F8zxgtl+3ILb3Vfm8wk3nQi30NmifJtRW0ccRqz3tH1fzxf9Bu2NIQLzj9maAAAAAElFTkSuQmCC&quot;)"><svg style="width:100%;height:auto;max-width:100%;margin-bottom:-4px" width="447" height="730"></svg><noscript><img style="width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0" src="/assets/ideal-img/21.eb8ad37.447.png" srcset="/assets/ideal-img/21.eb8ad37.447.png 447w" width="447" height="730"></noscript></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="마무리">마무리<a class="hash-link" href="#마무리" title="제목으로 바로 가기">​</a></h2><p>글의 내용이 JSP Project에 Gradle을 도입하는 것을 중점으로 작성되었다.</p><p><code>CI/CD</code> 는 특정 이벤트 발생 시 추가적으로 작업해주는 Agent 머신이라고 생각해도 된다. 머신이기 때문에 GUI와 같이 사람으로 하는 작업을 명령할 줄 알아야 하는데 JSP Project에서 이 방법을 몰라서 고생 고생하여 <code>Gradle</code> 을 사용하는 방법으로 진행되었다.</p><p>이제 원하는 <code>CI/CD</code> Service를 이용해서 JSP Project를 Build 하고 추가 적업을 진행할 수 있을 것이다.</p><p>읽어주셔서 감사합니다 :)</p><h2 class="anchor anchorWithHideOnScrollNavbar_GLuy" id="reference">Reference<a class="hash-link" href="#reference" title="제목으로 바로 가기">​</a></h2><ol><li><a href="https://medium.com/@jyson88/gradle-web-application-%EB%A7%8C%EB%93%A4%EA%B8%B0-677ea073599a" target="_blank" rel="noopener noreferrer">Eclipse에서 디렉터리 구조와 사용되는 라이브러리 및 JSP를 이용하여 빌드자동화 도구인 그레이드 사용법이 잘 나와있음</a></li><li><a href="https://fabxoe.tistory.com/15" target="_blank" rel="noopener noreferrer">톰캣 9.0 버전 이상 servlet-api 오류 해결 방법</a></li><li><a href="https://hyeonguj.github.io/2019/02/14/spring-boot-2-x-jsp-gradle-%EB%82%B4%EC%9E%A5%ED%86%B0%EC%BC%93-%EC%85%8B%ED%8C%85%ED%95%98%EA%B8%B0/" target="_blank" rel="noopener noreferrer">Spring boot를 통해서 JSP를 연결해야하는 이유</a></li><li>Spring Boot와 jsp 연동법<ol><li><a href="https://mia-dahae.tistory.com/131" target="_blank" rel="noopener noreferrer">해당 링크로 성공함</a></li><li><a href="https://galid1.tistory.com/434" target="_blank" rel="noopener noreferrer">application properties(application.yaml) 설정법만 확인</a></li></ol></li><li><a href="https://blog.naver.com/PostView.nhn?blogId=sharplee7&amp;logNo=221416943652" target="_blank" rel="noopener noreferrer">library 종속성 추가: jetty 서버를 이용한 방법</a></li><li><a href="https://olive-devlog.tistory.com/3" target="_blank" rel="noopener noreferrer">Docker tomcat 8.5 컨테이너에 웹 어플리케이션(war) 배포하기</a></li></ol>]]></content>
        <category label="JSP" term="JSP"/>
        <category label="CI/CD" term="CI/CD"/>
        <category label="IntellJ IDEA" term="IntellJ IDEA"/>
        <category label="Gradle" term="Gradle"/>
        <category label="DevOps" term="DevOps"/>
    </entry>
</feed>