<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>bab_shunn.log</title>
        <link>https://velog.io/</link>
        <description>마트 시식코너같은 저의 벨로그에 어서오세요.</description>
        <lastBuildDate>Mon, 04 May 2026 11:31:39 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>bab_shunn.log</title>
            <url>https://velog.velcdn.com/images/bab_shunn/profile/157c86f3-9db9-46cb-bfaf-01834628cea1/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. bab_shunn.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/bab_shunn" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[0부터 시작하는 오픈스택의 x신] 2주차 과제 상세 분석: 컬럼을 기본 출력에 추가]]></title>
            <link>https://velog.io/@bab_shunn/0%EB%B6%80%ED%84%B0-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-%EC%98%A4%ED%94%88%EC%8A%A4%ED%83%9D%EC%9D%98-x%EC%8B%A0-2%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C-%EC%83%81%EC%84%B8-%EB%B6%84%EC%84%9D-%EC%BB%AC%EB%9F%BC%EC%9D%84-%EA%B8%B0%EB%B3%B8-%EC%B6%9C%EB%A0%A5%EC%97%90-%EC%B6%94%EA%B0%80</link>
            <guid>https://velog.io/@bab_shunn/0%EB%B6%80%ED%84%B0-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-%EC%98%A4%ED%94%88%EC%8A%A4%ED%83%9D%EC%9D%98-x%EC%8B%A0-2%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C-%EC%83%81%EC%84%B8-%EB%B6%84%EC%84%9D-%EC%BB%AC%EB%9F%BC%EC%9D%84-%EA%B8%B0%EB%B3%B8-%EC%B6%9C%EB%A0%A5%EC%97%90-%EC%B6%94%EA%B0%80</guid>
            <pubDate>Mon, 04 May 2026 11:31:39 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우린 이전 포스팅에서 <a href="https://velog.io/@bab_shunn/0%EB%B6%80%ED%84%B0-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-%EC%98%A4%ED%94%88%EC%8A%A4%ED%83%9D%EC%9D%98-x%EC%8B%A0-OpenStackClient-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85">OpenStackClient 개발 환경 세팅</a>
<code>openstack server list</code> 명령어를 통해 인스턴스들을 확인해 보았습니다</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/22e46816-3a7b-4c3c-b413-9f14f94b70a4/image.png" alt=""></p>
<h3 id="미션-1">미션 1</h3>
<p>openstack server list 결과에 <code>user name</code>과 <code>project name</code> 이라는 컬럼을 기본 출력에 추가하고, 그 값을 출력하도록 만들어주세요
<img src="https://velog.velcdn.com/images/bab_shunn/post/c6c78ef3-03b7-4815-9a4e-4aee658a087b/image.png" alt=""></p>
<h3 id="미션-2">미션 2</h3>
<p>미션 1에서 컬럼을 추가하면, 실패하는 테스트 케이스가 다수 발생합니다.
모든 테스트 케이스가 통과하도록, 테스트 케이스 자체를 수정해주세요</p>
<p>조건) 새로 추가한 컬럼이 기본 값인 상태입니다. 이 컬럼이 기본인 조건을 만들고 테스트 케이스를 수정해야합니다</p>
<hr>
<h3 id="①-어느-부분을-수정해야-할까">① 어느 부분을 수정해야 할까?</h3>
<p>server list 라는 명령어를 처리하는 <code>python-openstackclient/openstackclient/compute/v2/server.py</code> 부분을 살펴보겠습니다
<img src="https://velog.velcdn.com/images/bab_shunn/post/cbf4a504-1997-4bed-8000-df0a42cf4445/image.png" alt="">
이 함수가 칼럼에 관여하는 친구인거 같다 <del><code>column_headers</code> 투성이</del>
<code>server.py</code> 파일</p>
<pre><code class="language-py">            columns = (
                &#39;ID&#39;,
                &#39;Name&#39;,
                &#39;Status&#39;,
                &#39;Networks&#39;,
                &#39;Image Name&#39;,
                &#39;Flavor Name&#39;,
            )
        column_headers = (
            &#39;ID&#39;,
            &#39;Name&#39;,
            &#39;Status&#39;,
            &#39;Networks&#39;,
            &#39;Image&#39;,
            &#39;Flavor&#39;,
        )
</code></pre>
<p>이런식으로 담겨있을 줄 알았으나,,,</p>
<pre><code class="language-py">             columns: tuple[str, ...] = (
                &#39;id&#39;,
                    &#39;name&#39;,
                &#39;status&#39;,
            )
        column_headers: tuple[str, ...] = (
            &#39;ID&#39;,
            &#39;Name&#39;,
            &#39;Status&#39;,
        )</code></pre>
<p><del>어림도 없지 암</del> 이렇게 되어있어서 봤더니</p>
<pre><code class="language-py">        if sdk_utils.supports_microversion(compute_client, &#39;2.47&#39;):
            columns += (&#39;flavor_name&#39;,)
            column_headers += (&#39;Flavor&#39;,)
        else:
            if parsed_args.long:
                columns += (
                    &#39;flavor_name&#39;,
                    &#39;flavor_id&#39;,
                )
                column_headers += (
                    &#39;Flavor Name&#39;,
                    &#39;Flavor ID&#39;,
                )
            else:
                if parsed_args.no_name_lookup:
                    columns += (&#39;flavor_id&#39;,)
                else:
                    columns += (&#39;flavor_name&#39;,)
                column_headers += (&#39;Flavor&#39;,)

        #새로 추가한 부분
        columns += (
            &#39;project_name&#39;,
            &#39;user_name&#39;,
        )
        column_headers += (
            &#39;Project Name&#39;,
            &#39;User Name&#39;,
        )
</code></pre>
<p>명령어에 따라 <strong>동적으로 추가</strong>시키려고 코드가 이런식으로 작성되어있었다.
<code>project_name</code>이나 <code>user_name</code> 같은 경우 조건 기반 동적 구성할 필요는 없어서 이렇게 코드를 추가해줬습니다</p>
<blockquote>
<p>이제 ListServer의 기본 columns에 <code>project_name</code>과 <code>user_name</code>을 추가했고,
server list의 기본 출력 순서가 기존 
ID, Name, Status, Networks, Image, Flavor에서 
ID, Name, Status, Networks, Image, Flavor, Project Name, User Name으로 바뀝니다.</p>
</blockquote>
<p>+) 추가적으로</p>
<pre><code class="language-py">if parsed_args.columns:
            for c in parsed_args.columns:
                if c in (&#39;Project ID&#39;, &#39;project_id&#39;):
                    columns += (&#39;project_id&#39;,)
                    column_headers += (&#39;Project ID&#39;,)
                if c in (&#39;User ID&#39;, &#39;user_id&#39;):
                    columns += (&#39;user_id&#39;,)
                    column_headers += (&#39;User ID&#39;,)
                    ...
                #새로 추가한 부분
                if c in (&#39;Project Name&#39;, &#39;project_name&#39;):
                    columns += (&#39;project_name&#39;,)
                    column_headers += (&#39;Project Name&#39;,)
                if c in (&#39;User Name&#39;, &#39;user_name&#39;):
                    columns += (&#39;user_name&#39;,)
                    column_headers += (&#39;User Name&#39;,)</code></pre>
<p>같은 파일에 컬럼 옵션 처리가 되어 있는 코드가 보이는데,
Project Name과 User Name을 넣어서, <strong>-c 옵션으로도 선택 가능</strong>하게 했습니다. </p>
<h3 id="②-반환-되는-것들을-확인해보자">② 반환 되는 것들을 확인해보자</h3>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Status</th>
<th>Networks</th>
<th>Image</th>
<th>Flavor</th>
<th>User Name</th>
<th>Project Name</th>
</tr>
</thead>
<tbody><tr>
<td>6cae97c9-eb91-4834-9ec4-b6a7d239bfc1</td>
<td>test_instance_user10</td>
<td>ACTIVE</td>
<td>shared=192.168.233.41</td>
<td>cirros-0.6.3-x86_64-disk</td>
<td>m1.nano</td>
<td>e4d2d84dfd764831a7f4a78affe3e7c2</td>
<td>f6351174806d45e98876063199f447af</td>
</tr>
</tbody></table>
<p>보다시피 새로 추가한 칼럼인 유저 네임과 프로젝트 네임이 굉장히 러프(?)하다
여기서 필요하다고 느끼는건 저 값을 받아서 대조하여 <code>출력값을 채워줄 무언가</code>가 필요하다.</p>
<blockquote>
<p>즉, OpenStack의 기본 서버 응답에는 user_id, project_id만 포함되어 있으며,
<strong>실제 사람이 읽을 수 있는 이름(name)은 별도의 Identity 서비스 조회</strong>를 통해 가져와야 한다.</p>
</blockquote>
<pre><code class="language-py">project_names = {}
        user_names = {}
        if data:
            for project_id in {
                s.project_id for s in data if getattr(s, &#39;project_id&#39;, None)
            }:
                try:
                    project_names[project_id] = identity_common.find_project(
                        identity_client,
                        project_id,
                    ).name
                except Exception:  # noqa: S110
                    # retrieving project names is not crucial, so we swallow
                    # any exceptions
                    pass

            for user_id in {
                s.user_id for s in data if getattr(s, &#39;user_id&#39;, None)
            }:
                try:
                    user_names[user_id] = identity_common.find_user(
                        identity_client,
                        user_id,
                    ).name
                except Exception:  # noqa: S110
                    # retrieving user names is not crucial, so we swallow any
                    # exceptions
                    pass</code></pre>
<p><code>출력 값 채우는 로직</code>에서는 각 서버의 project_id와 user_id를 모아 <code>identity_common.find_project</code>와 <code>identity_common.find_user</code>로 이름을 조회한 뒤, server 객체의 project_name과 user_name에 넣도록 구현하였다. 이미지와 플레버 이름 조회 흐름은 그대로 유지했고, 새 컬럼만 추가로 계산하는 구조이다</p>
<p>+) 근데 이게 정말 최선일까?
분명 기존 코드에 데이터를 가져오는 로직이 있을것이다. </p>
<blockquote>
<p>&quot;<em>서버 단위 데이터 처리 흐름을 통합하여 응집도를 높이고, 조건 기반 로직과 캐싱을 자연스럽게 적용하기 위한 구조 개선을 하고싶다...</em>&quot; 라는 생각이 들때쯤</p>
</blockquote>
<pre><code class="language-py"> # Populate image_name, image_id, flavor_name and flavor_id attributes
        # of server objects so that we can display those columns.
        project_names = {}
        user_names = {}
        for s in data:
            if sdk_utils.supports_microversion(compute_client, &#39;2.69&#39;):
                if getattr(s, &#39;status&#39;) == &#39;UNKNOWN&#39;:
                    continue

            if &#39;id&#39; in s.image and s.image.id is not None:
                image = images.get(s.image[&#39;id&#39;])
                if image:
                    s.image_name = image.name
                s.image_id = s.image[&#39;id&#39;]
            else:            
                s.image_name = IMAGE_STRING_FOR_BFV
                s.image_id = IMAGE_STRING_FOR_BFV
            ## 해당 for문 내부에 코드를 구현했다
            project_id = getattr(s, &#39;project_id&#39;, None)
            if project_id and project_id not in project_names:
                try:
                    project_names[project_id] = identity_common.find_project(
                        identity_client,
                        project_id,
                    ).name
                except Exception:  # noqa: S110
                    pass
            s.project_name = project_names.get(project_id)

            user_id = getattr(s, &#39;user_id&#39;, None)
            if user_id and user_id not in user_names:
                try:
                    user_names[user_id] = identity_common.find_user(
                        identity_client,
                        user_id,
                    ).name
                except Exception:  # noqa: S110
                    pass
            s.user_name = user_names.get(user_id)
</code></pre>
<p>합치려고 보니
서버마다 <code>find_project</code>와 <code>find_user</code>를 매번 호출하면? 
-&gt; 오히려 중복 조회가 늘어나지 않을까...?</p>
<p>그래서 루프를 합치되 ID별 캐시는 유지하는 방향으로 개선을 시도 
즉, 지금의 딕셔너리 캐시 방식은 유지하고, 그 캐시 채우기와 서버 객체 값 세팅을 같은 for s in data: 안에서 처리하는 쪽이 균형이 좋을 거 같아 구조화를 해보았다</p>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/1b1ce774-95c9-43c1-8055-97888dfcf73b/image.png" alt=""></p>
<p><span style="color:olivedrab"><em>사실 실제로 기여하는 사람들 입장에선 저 밈 수준으로 매우 간단한 작업이고 당연한 수순일 순 있다.
하지만 난 <code>오픈소스 기여 호소인</code>이므로 자세하게 설명할거다.</em></span></p>
<p>정리) 단도직입적으로 남들이 &quot;그래서 그냥 비슷한 기능의 for문 찾아서 내부에 포함시켰다는 거냐?&quot;라고 질문한다면 무시하지말고 이렇게 답변하면 된다</p>
<p>첫번째 구현 방식: 캐시를 먼저 만들고, 나중에 한 번 더 서버를 순회
개선한 방식: 한 번의 서버 순회 안에서 캐시 조회/갱신과 <code>s.project_name</code>, <code>s.user_name</code> 세팅을 같이 수행</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[0부터 시작하는 오픈스택의 x신] 1주차 과제 상세 분석: openstack server show는 어떻게 작동하는가?]]></title>
            <link>https://velog.io/@bab_shunn/0%EB%B6%80%ED%84%B0-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-%EC%98%A4%ED%94%88%EC%8A%A4%ED%83%9D%EC%9D%98-x%EC%8B%A0-1%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C-%EC%83%81%EC%84%B8-%EB%B6%84%EC%84%9D-openstack-server-show%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%9E%91%EB%8F%99%ED%95%98%EB%8A%94%EA%B0%80</link>
            <guid>https://velog.io/@bab_shunn/0%EB%B6%80%ED%84%B0-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-%EC%98%A4%ED%94%88%EC%8A%A4%ED%83%9D%EC%9D%98-x%EC%8B%A0-1%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C-%EC%83%81%EC%84%B8-%EB%B6%84%EC%84%9D-openstack-server-show%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%9E%91%EB%8F%99%ED%95%98%EB%8A%94%EA%B0%80</guid>
            <pubDate>Sun, 26 Apr 2026 07:05:40 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>해당 포스팅은 1주차 과제였던 <em>&quot;개발환경에서 openstack server list 명령어 실행 결과 확인&quot;</em> 을 기반으로 내부적으로 코드가 어떻게 흐르는지 파악하는 것입니다</p>
</blockquote>
<p>방향 설정: nova 의 정보를 어느시점에서 가져오고 렌더링할 서버 정보를 가져오는 건 take_action 의 어느 시점인지를 중점으로 분석</p>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/5d13afc5-32fc-44f8-8074-d90ec4bf279c/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/40024729-c6c7-4b7c-a990-1dfba68fa019/image.png" alt=""></p>
<h2 id="작은-강의-이런-오픈소스코드-분석은-어떻게-하면-좋은가">[작은 강의] 이런 오픈소스코드 분석은 어떻게 하면 좋은가?</h2>
<p><code>openstack --debug server list</code></p>
<p>해당 로그들 속에서</p>
<p><code>greb -R &quot;{궁금한 문자열}&quot;</code></p>
<p>만약 검색이 안된다면 가상환경에 설치된 라이브러리들이 호출을 통해 문자열을 출력하고 있는것</p>
<p>가상환경으로 들어가서 <code>greb -R &quot;{궁금한 문자열}&quot;</code></p>
<p>호출이 되고 있는 경로를 찾았다면, </p>
<p>해당 경로 파일을 열어서 hook, 함수 등 종속적으로 호출하고 있는 것을 확인</p>
<p>만약, 속한 class 를 확인 하였다면 해당 클래스 검색 -&gt; 호출하고 있는 것들 중 진짜 내가 찾는 것과 관련된 것들 걸러내기</p>
<p>=&gt; 단편적인 요소를 찾고 상향식으로 분석</p>
<p>추천) <a href="https://wikidocs.net/230415">https://wikidocs.net/230415</a> traceback를 사용하면 좀 더 수월하게 확인이 가능</p>
<p>주요 라이브러리</p>
<ul>
<li>OSC_lib ( 다른 lib, cli, sdl)</li>
<li>stevedore ( 동적 louder )( csl 에서 사용하기 위한 것들을 동적으로 불러와 사용하기 위한 것들 )</li>
<li>keystoneouth ( 인증 관련)</li>
</ul>
<p>-&gt; stevedore, keystoneouth 에 빠지면 return 값 정도만 확인하고 빠져나오세요 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[0부터 시작하는 오픈스택의 x신] OpenStackClient 개발 환경 세팅]]></title>
            <link>https://velog.io/@bab_shunn/0%EB%B6%80%ED%84%B0-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-%EC%98%A4%ED%94%88%EC%8A%A4%ED%83%9D%EC%9D%98-x%EC%8B%A0-OpenStackClient-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85</link>
            <guid>https://velog.io/@bab_shunn/0%EB%B6%80%ED%84%B0-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-%EC%98%A4%ED%94%88%EC%8A%A4%ED%83%9D%EC%9D%98-x%EC%8B%A0-OpenStackClient-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85</guid>
            <pubDate>Fri, 24 Apr 2026 17:18:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>OpenStack CLI 클라이언트(python-openstackclient)를 로컬에서 직접 개발하고 디버깅하기 위한 <strong>VSCode</strong> 환경 구축 방법 (+근데 <strong>윈도우</strong> 환경을 곁들인...)</p>
</blockquote>
<p>이 게시물을 읽고 계신다면, 분명 주위에서 파이참을 사용하라고 권유했을거고 당신의 윈도우 환경에 안타까움을 표했을겁니다...</p>
<p>그럼에도 불구하고 꿋꿋하게 환경세팅을 하려고 제 게시물을 읽고 있는 당신에게 경의를 표하며... <del>지금이라도 주변의 말을 들으세요</del></p>
<p>_<span style="color:olivedrab">참고) 본격적인 포스팅에 앞서 윈도우 명령어 앞엔 🪟으로 기입하겠습니다</span>_</p>
<hr>
<h3 id="환경세팅을-하기에-앞서-간략한-사전-지식-및-전체-흐름">[환경세팅을 하기에 앞서, 간략한 사전 지식 및 전체 흐름]</h3>
<ol>
<li>클라우드 컴퓨팅은 원격지의 리소스를 API 호출을 통해 제어합니다. 
사용자는 웹 대시보드(GUI)를 사용할 수도 있지만, 
개발 생산성과 자동화를 위해 CLI(Command Line Interface) 도구인 <code>python-openstackclient</code>를 사용</li>
</ol>
<p>-&gt; 마치 쿠버네티스의 kubectl과 비슷한 역할이랄까</p>
<ol start="2">
<li>해당 프로젝트는 파이썬을 사용합니다. 그러므로 의존성 문제를 예방하기 위해 가상환경을 사용합니다 -&gt; 시스템 전역에 패키지 설정하면^^..... <del>버전 충돌쇼</del> </li>
</ol>
<p>-&gt; 정리하자면 가상환경(내방) &gt; 라이브러리 모음집 설치 &gt; 구동</p>
<hr>
<h4 id="①-소스코드-clone">① 소스코드 clone</h4>
<p>자신의 작업 디렉토리에 python-openstackclient 코드를 clone 받습니다.
<code>git clone https://opendev.org/openstack/python-openstackclient</code></p>
<h4 id="②-cloudsyaml-생성">② clouds.yaml 생성</h4>
<p>python-openstackclient를 clone 받은 위치에 아래 내용으로 clouds.yaml 파일을 생성합니다.</p>
<pre><code class="language-yaml">clouds:
  my-cloud:
    auth:
      auth_url: http://&lt;ip&gt;/identity
      username: &quot;admin&quot; # 할당 받은 값 넣으세요
      password: &quot;1234&quot; # 할당 받은 값 넣으세요
      project_name: &quot;admin&quot; # 할당 받은 값 넣으세요
      user_domain_name: &quot;Default&quot;
      project_domain_name: &quot;Default&quot;
    region_name: &quot;RegionOne&quot;
    interface: &quot;public&quot;
    identity_api_version: &quot;3&quot;</code></pre>
<h4 id="③-visual-studio-code에서-python-openstackclient-폴더-열기">③ Visual Studio Code에서 python-openstackclient 폴더 열기</h4>
<p>visual studio code를 이용할 경우, IDE로서의 기능보다 코드를 분석하고 수정하는 용도로 주로 활용하게 됩니다. 따라서, openstackclient 실행과 로그 분석은 터미널에서 합니다.
code내에서 터미널을 열고 작업을 이어갑니다.</p>
<blockquote>
<p>vscode 자체를 관리자 권한으로 설치하면 git bash도 관리자 권한으로 내부에서 사용가능하다고 Reddit에 토론기록이 남아있긴한데, <del>그 누구도 성공적인 시도였다는 후기가 없다</del> <span style="color:olivedrab">옆에 git bash 관리자 권한으로 열어서 작업하는 걸 추천 드립니다</span></p>
</blockquote>
<h4 id="④-virtualenv-생성-가상-환경-만들고-만든-환경에-들어가기">④ virtualenv 생성 (가상 환경 만들고 만든 환경에 들어가기)</h4>
<blockquote>
<p><code>python3</code> -&gt; <code>python</code>: 윈도우 환경에서는 python 명령어가 3.x 버전을 가리키는 기본 실행 파일명이므로 교체</p>
</blockquote>
<blockquote>
<p><code>.venv/bin/activate</code> -&gt; <code>.venv/Scripts/activate</code>: 윈도우 파이썬 가상 환경은 실행 파일(Script)을 bin이 아닌 Scripts 디렉토리에 저장</p>
</blockquote>
<p>아래 명령어로 virtualenv를 생성합니다.
<code>python3 -m venv .venv</code>
🪟<code>python -m venv .venv</code>
그리고 virtualenv 를 활성화합니다.
<code>source .venv/bin/activate</code>
🪟<code>source .venv/Scripts/activate</code> -&gt; ex) 이게 가상환경(내 방) 가는 길인거다 
정상적으로 활성화되었다면 다음과 같이 터미널에 표시됩니다.
<img src="https://velog.velcdn.com/images/bab_shunn/post/59256475-ccb3-43b3-af10-7c28cdf1b59b/image.png" alt=""></p>
<h4 id="⑤-python-openstackclient-의-의존-라이브러리-설치하기">⑤ python-openstackclient 의 의존 라이브러리 설치하기</h4>
<blockquote>
<p><code>pip3</code>-&gt;<code>pip</code>: 리눅스와 달리 윈도우 가상 환경(.venv/Scripts)에는 pip3.exe 파일이 생성되지 않는 경우가 많습니다. </p>
</blockquote>
<p>이제 의존 라이브러리를 설치합니다.
아래 명령어로 의존 라이브러리를 설치합니다.
<code>pip3 install -r requirements.txt</code> -&gt; 실행용
🪟<code>pip install -r requirements.txt</code> 
<code>pip3 install -r test-requirements.txt</code> -&gt; 테스트나 코드 품질 검사용
🪟<code>pip install -r test-requirements.txt</code> </p>
<h4 id="⑥-python-openstackclient를-develop-모드로-설치하기">⑥ python-openstackclient를 develop 모드로 설치하기</h4>
<p>이제 virtualenv 에 python-openstackclient 자체를 설치합니다.
일반적으로 파이썬 코드를 받아 설치할 때는 <code>python setup.py install</code>을 이용하지만, 개발을
위해서는 아래 명령어로 develop 모드로 설치해아 합니다.
<code>python3 setup.py develop</code>
🪟<code>python setup.py develop</code></p>
<h4 id="⑦-openstack-server-list-실행해보기">⑦ openstack server list 실행해보기</h4>
<p>python-openstackclient의 main 함수는 openstackclient/shell.py 에 있습니다.
하지만 PyCharm 과 다르게 우리는 openstackclient를 virtualenv에 설치했으므로 openstack
명령어를 그대로 사용해도 됩니다.
아래 명령어로 결과를 확인해봅니다.
<code>openstack server list --os-cloud my-cloud</code>
<img src="https://velog.velcdn.com/images/bab_shunn/post/01cf23f0-cc11-4177-967c-57c043a19770/image.png" alt="">
이 때 인스턴스 이름은 test_instance_계정명 으로 된 것이 나와야합니다.
여기까지 되었다면 openstackclient 코드를 개발할 준비가 완료되었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋사] 리액트 라우터/클라이언트 라우팅 직접 구현]]></title>
            <link>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%8C%85-%EC%A7%81%EC%A0%91-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%8C%85-%EC%A7%81%EC%A0%91-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Tue, 17 Jun 2025 09:00:00 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/bab_shunn/post/118a8864-11e6-436c-9327-0a94db4f07ae/image.png" alt=""></p>
<ul>
<li>routes.tsx</li>
</ul>
<pre><code class="language-js">import { createBrowserRouter } from &quot;react-router&quot;;
import Home from &quot;@pages/Home&quot;;
import Page1 from &quot;@pages/Page1&quot;;
import Page2 from &quot;@pages/Page2&quot;;

const router = createBrowserRouter([
  { path: &#39;/home&#39;, element: &lt;Home /&gt; },
  { path: &#39;/page1&#39;, element: &lt;Page1 /&gt; }, 
  { path: &#39;/page2&#39;, element: &lt;Page2 /&gt; },
]);

export default router;</code></pre>
<ul>
<li>App.tsx</li>
</ul>
<pre><code class="language-js">import { RouterProvider } from &#39;react-router&#39;;
import router from &#39;./routes&#39;;

function App() {
  return (
    &lt;&gt;
      &lt;RouterProvider router={ router } /&gt;
    &lt;/&gt;
  );
}

export default App;</code></pre>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/5e67bb70-d478-4c59-920f-abf653147ff7/image.png" alt=""></p>
<ul>
<li>Header.tsx<pre><code class="language-js">import { Link } from &quot;react-router&quot;;
</code></pre>
</li>
</ul>
<p>function Header() {
  return (
    <header>
      <h1>02 리액트 라우터 사용</h1>
      <Link to="/home">home</Link><br/>
      <Link to="/page1">page1</Link><br/>
      <Link to="/page2">page2</Link>
    </header>
  );
}</p>
<p>export default Header;</p>
<pre><code>
![](https://velog.velcdn.com/images/bab_shunn/post/160998d1-9775-4b6b-8c59-bbbbf77f6e8f/image.png)
![](https://velog.velcdn.com/images/bab_shunn/post/398acf05-e81c-4194-bd5c-b2dc16e000f4/image.png)
![](https://velog.velcdn.com/images/bab_shunn/post/15d64cc6-dc16-4caa-9f44-a6f88d6fa749/image.png)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[멋사] API fetch/custom hook 구현]]></title>
            <link>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-API-fetchcustom-hook-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-API-fetchcustom-hook-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Mon, 16 Jun 2025 08:57:21 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/bab_shunn/post/018fd4fa-a45b-406c-afad-84857ed14151/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/6bf8d2a7-6cde-43b3-8a8c-e9235b8b331b/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/c6f84f25-9aae-489b-ab67-01be3c61018e/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/15b0dca9-04d5-4e61-96eb-87c71718ad24/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/54d40e68-1cdb-4e7a-af94-71e05dab6414/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/728690b3-d701-4411-a699-e6ef12645aa6/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/7e2e89c6-631e-43d4-98bc-19a7298de5f5/image.png" alt=""></p>
<p>물론이야. 아래는 <code>fetch</code> 직접 사용과 <code>useFetch</code> 커스텀 훅을 비교 정리한 velog용 글 형식의 간단한 개념 정리야:</p>
<hr>
<h2 id="fetch-직접-사용-vs-커스텀-훅usefetch-사용-비교">fetch 직접 사용 vs 커스텀 훅(useFetch) 사용 비교</h2>
<h3 id="1-fetch-직접-사용">1. fetch 직접 사용</h3>
<h4 id="개요">개요</h4>
<p>React 컴포넌트 내부에서 <code>fetch()</code> 함수를 직접 호출하여 데이터를 요청하고, <code>useState</code>, <code>useEffect</code> 등을 통해 상태를 관리하는 방식.</p>
<h4 id="장점">장점</h4>
<ul>
<li>코드 흐름이 직관적이고 이해하기 쉬움</li>
<li>API 요청마다 다른 로직을 유연하게 작성 가능</li>
</ul>
<h4 id="단점">단점</h4>
<ul>
<li>여러 컴포넌트에서 동일한 요청을 구현할 경우 코드 중복이 많아짐</li>
<li>로딩, 에러, 데이터 상태 등을 매번 직접 관리해야 함</li>
<li>UI와 비즈니스 로직이 섞여 코드가 복잡해질 수 있음</li>
</ul>
<hr>
<h3 id="2-usefetch-커스텀-훅-사용">2. useFetch 커스텀 훅 사용</h3>
<h4 id="개요-1">개요</h4>
<p>API 요청 및 관련 상태 관리(<code>loading</code>, <code>error</code>, <code>data</code>)를 커스텀 훅으로 분리하여 관리하는 방식. 주로 재사용성과 관심사 분리를 위해 사용함.</p>
<h4 id="장점-1">장점</h4>
<ul>
<li>로직 재사용이 가능하여 코드 중복 최소화</li>
<li>로딩, 에러 처리 등의 상태 관리가 일관됨</li>
<li>컴포넌트는 UI에만 집중할 수 있어 코드 구조가 깔끔해짐</li>
<li>유지보수가 쉬움 (훅 하나만 수정하면 됨)</li>
</ul>
<h4 id="단점-1">단점</h4>
<ul>
<li>초기에 구조를 이해하고 설정하는 데 시간이 필요함</li>
<li>훅 내부를 커스터마이징하려면 추가 작업이 필요할 수 있음</li>
<li>단순한 요청에도 훅 사용이 과할 수 있음</li>
</ul>
<hr>
<h3 id="결론">결론</h3>
<ul>
<li>작은 프로젝트나 간단한 요청에는 <code>fetch</code> 직접 사용이 편리</li>
<li>여러 컴포넌트에서 같은 API를 호출하거나 상태 관리가 필요한 경우에는 <code>useFetch</code> 커스텀 훅을 사용하는 것이 효율적</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋사] useReducer/useRef]]></title>
            <link>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-useReduceruseRef</link>
            <guid>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-useReduceruseRef</guid>
            <pubDate>Thu, 12 Jun 2025 08:55:23 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/bab_shunn/post/fbd45258-78ec-484c-9548-f61edfb4cde1/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/c5714463-dacf-4f59-a25c-f806bdc59241/image.png" alt=""><img src="https://velog.velcdn.com/images/bab_shunn/post/a31a0ef5-a294-49cf-8663-d5b78ae598b5/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/d7c0da3f-d29c-41db-85ac-936e94ec365a/image.png" alt=""></p>
<h2 id="3-usereducer">3) <code>useReducer</code></h2>
<p>useState와 역할은 비슷한데, useState가 제공하는 기능 이외에도 상태 업데이트 로직을 <u><strong>외부 함수로 분리할 수 있는 기능</strong></u>을 제공한다.</p>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/6615f8ec-d615-482c-9d2c-128b8f194538/image.png" alt=""></p>
<p>useState와 useReducer의 차이는 위 그림과 같다. <strong>상태를 관리한다는 목적은 동일</strong>한데 <strong>상태를 업데이트하는 방법이 약간 다르다.</strong>
useReducer는 미리 정의된 reducer 함수에 action과 이전 상태값을 입력값으로 넣어서 새로운 상태를 계산한다는 차이점이다.</p>
<hr>
<h2 id="4-useref">4) <code>useRef</code></h2>
<p>useRef는 <u><strong>특정 DOM에 접근하여 DOM 조작</strong></u>을 가능하게 하는 훅이다.
리액트 프로젝트에서도 특정 요소를 선택해야하는 상황이 생길 수 있는데 이러한 상황에서 useRef 함수를 사용할 수 있게 된다.</p>
<br/>

<p>가장 큰 특징은 useRef는 따로 상태 변경 함수가 없고 직접 <code>.current</code>의 상태를 변경하기 때문에 <u><strong>변경 사항이 자동으로 브라우저 화면에 반영되지 않는다는 점</strong></u>이다.</p>
<br/>

<p>하지만 컴포넌트 지역 변수와 다르게 useRef 상태는 컴포넌트가 언마운트될 때까지 유지되기 때문에, 여러 이유로 인해 해당 컴포넌트 render 함수가 실행되면 useRef 변경 사항이 화면에 반영될 수 있다.</p>
<pre><code class="language-js">{/* 변경 사항이 자동으로 화면에 반영된다. */}
&lt;button onClick={() =&gt; setCount((prev) =&gt; prev + 1)}&gt;
  useState 상태 업데이트
&lt;/button&gt;

{/* 변경 사항이 자동으로 화면에 반영되지 않는다. 상태는 유지된다. */}
&lt;button onClick={() =&gt; reference.current++}&gt;
  useRef 상태 업데이트
&lt;/button&gt;</code></pre>
<br/>

<p>그래서 useRef는 주로 <strong>자동으로 화면을 렌더링하지 않아도 되는 상태</strong>를 관리할 때 사용한다.
화면 렌더링과 관련이 없는 상태를 useState로 관리하면 render 함수가 불필요하게 실행되기 때문이다.</p>
<h2 id="참고">참고:</h2>
<ul>
<li><a href="https://velog.io/@gwak2837/React-Hooks-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-2">React Hooks 이해하기 (2)</a> </li>
<li><a href="https://ko.legacy.reactjs.org/docs/hooks-intro.html">Hook의 개요</a></li>
<li><a href="https://choijying21.tistory.com/entry/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0React-Hooks%EB%9E%80">JDevelog:티스토리</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋사] 클래스 컴포넌트와 컴포넌트의 라이프 사이클/리액트 훅]]></title>
            <link>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%99%80-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%9D%98-%EB%9D%BC%EC%9D%B4%ED%94%84-%EC%82%AC%EC%9D%B4%ED%81%B4%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85</link>
            <guid>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%99%80-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%9D%98-%EB%9D%BC%EC%9D%B4%ED%94%84-%EC%82%AC%EC%9D%B4%ED%81%B4%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85</guid>
            <pubDate>Wed, 11 Jun 2025 08:35:01 GMT</pubDate>
            <description><![CDATA[<h2 id="컴포넌트-생명-주기">컴포넌트 생명 주기</h2>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/d2186af6-534c-4421-8b10-5a69e024faef/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/2b0dab47-3395-4ae4-af52-a3a5478eaeea/image.png" alt=""><img src="https://velog.velcdn.com/images/bab_shunn/post/f3f7115d-0dc1-43b9-98a0-5f7b54a7b78d/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/78a2ef85-5676-47d5-bb55-47ea851ec8e7/image.png" alt=""></p>
<hr>
<h2 id="리액트-훅">리액트 훅</h2>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/abbc6c80-065b-4eb6-89e8-b5ccb9daf30c/image.png" alt=""><img src="https://velog.velcdn.com/images/bab_shunn/post/b399a6c6-fe80-406c-ab40-736b217cf03e/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/800308cb-eee2-44d6-a1cb-fa6aa79de825/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋사] useState 기반 수동 검증 방식/react-hook-form을 사용한 선언적 방식/클래스형 컴포넌트]]></title>
            <link>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-js-useState-%EA%B8%B0%EB%B0%98-%EC%88%98%EB%8F%99-%EA%B2%80%EC%A6%9D-%EB%B0%A9%EC%8B%9Dreact-hook-form%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%84%A0%EC%96%B8%EC%A0%81-%EB%B0%A9%EC%8B%9D%ED%81%B4%EB%9E%98%EC%8A%A4%ED%98%95-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</link>
            <guid>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-js-useState-%EA%B8%B0%EB%B0%98-%EC%88%98%EB%8F%99-%EA%B2%80%EC%A6%9D-%EB%B0%A9%EC%8B%9Dreact-hook-form%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%84%A0%EC%96%B8%EC%A0%81-%EB%B0%A9%EC%8B%9D%ED%81%B4%EB%9E%98%EC%8A%A4%ED%98%95-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</guid>
            <pubDate>Tue, 10 Jun 2025 08:38:47 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/bab_shunn/post/0c5317f3-3098-4110-a143-7c0cbcf6148c/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/aa5a0258-acfb-4bb0-9b03-b8f47cd7320b/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/aa6961b7-c3b0-4fdf-8a0c-4cea85ad0f8e/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/5c3a6991-c92f-493e-8cf0-9744f6a6f3e9/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/1fafc08b-6228-4f2b-80e5-4907b70df57f/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋사] state 관리 시작]]></title>
            <link>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-js-state-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-js-state-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Mon, 09 Jun 2025 09:23:25 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/bab_shunn/post/f8e7ce90-51f5-4c1f-b9cf-6d8efd570a0c/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/95898e04-55f5-4d5b-b545-903bf5ec555b/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/46340f41-035f-4a90-9089-856ac6658cfa/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋사] react start]]></title>
            <link>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-react-start</link>
            <guid>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-react-start</guid>
            <pubDate>Wed, 04 Jun 2025 08:44:59 GMT</pubDate>
            <description><![CDATA[<h3 id="리액트-관련-개념들과-특징-정리-25-06-04">리액트 관련 개념들과 특징 정리 (25-06-04)</h3>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/c9e1de32-0527-408a-b373-5d3307233a48/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋사] 리액트 시작?]]></title>
            <link>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%8B%9C%EC%9E%91</link>
            <guid>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%8B%9C%EC%9E%91</guid>
            <pubDate>Fri, 30 May 2025 09:01:32 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/bab_shunn/post/4933deef-7003-42a4-b9ad-b9748f96ba4c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/36f2649b-60da-4db5-bfe3-d021d521f1e9/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/140ecda9-064c-4069-bdd3-1a3cd937112d/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/da202d4a-762a-44e3-be92-6277f173b69a/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/f294dbd2-416b-47f4-849b-64ee6174f0a3/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/30a08338-8823-4ab2-8341-ac738fda826f/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/6ed90810-2d35-4b25-a223-2d9e47cea88c/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋사] React 빌드업 ]]></title>
            <link>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-React-%EB%B9%8C%EB%93%9C%EC%97%85</link>
            <guid>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-React-%EB%B9%8C%EB%93%9C%EC%97%85</guid>
            <pubDate>Tue, 27 May 2025 08:05:01 GMT</pubDate>
            <description><![CDATA[<p>1장 리액트 빌드업</p>
<ul>
<li><ol>
<li>웹 개발의 변천사</li>
</ol>
</li>
<li><ol start="2">
<li>리액트 개발에 자주 사용하는 자바스크립트 문법
<img src="https://velog.velcdn.com/images/bab_shunn/post/90a11512-b7b0-47b4-9e5b-335d1f81d5ac/image.jpeg" alt=""></li>
</ol>
</li>
<li><p>간단한 todoList 실습 (dom 객체를 다루는데에 좀 더 익숙해지기 위한)
<img src="https://velog.velcdn.com/images/bab_shunn/post/c0fe6655-bb63-4793-937a-30d8b8d72eb6/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/d5c9b6cd-9c43-455a-b2ca-c67ade038db7/image.png" alt="">
<img src="https://velog.velcdn.com/images/bab_shunn/post/455185ee-cced-462e-a990-b67dc31aeb72/image.png" alt=""></p>
</li>
<li><p>간단한 counter 실습 (dom 객체를 다루는 코드는 js 파일에서 관리하고 해당 코드를 함수로 만들어 html 파일에서 dom조작을 함수를 통해 더 간결한 코드를 작성합니다. 마지막엔 코드를 하나의 함수로 만들고, 좀 더 유연한 사용을 위해 컴포넌트로 쪼개어 사용할 수 있는 것들을 함수로 만들어 최종적으로 rootNode에 추가합니다.)
<img src="https://velog.velcdn.com/images/bab_shunn/post/4416eea6-4f28-4e7a-8d0b-83656e56f5f4/image.png" alt=""></p>
</li>
</ul>
<p>+)사진이 깨져서 pdf로도 저장했는데 벨로그는 pdf가 안올라가네요...? 호호,,</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋사] -js 내장객체 object]]></title>
            <link>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-js-%EB%82%B4%EC%9E%A5%EA%B0%9D%EC%B2%B4-object</link>
            <guid>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-js-%EB%82%B4%EC%9E%A5%EA%B0%9D%EC%B2%B4-object</guid>
            <pubDate>Wed, 30 Apr 2025 04:39:49 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/bab_shunn/post/95770459-cd49-4149-bea5-de6a3bed99db/image.png" alt=""></p>
<ul>
<li><code>const haru2 = haru;</code> → <strong>얕은 복사(X)</strong> → <strong>참조 복사</strong></li>
<li><code>const haru3 = Object.assign({}, haru);</code> 또는 <code>{ ...haru }</code> → <strong>얕은 복사(O)</strong>  <blockquote>
<p>JavaScript 자체 내장 메서드로는 진정한 깊은 복사(deep copy)는 제공되지 않음! (라이브러리를 사용하거나 로데쉬..., json을 통해 부분적으로 구현 가능하긴함</p>
</blockquote>
</li>
</ul>
<hr>
<h4 id="haru2--haru"><code>haru2 = haru;</code></h4>
<ul>
<li><strong>같은 객체를 가리키는 <u>참조 복사</u></strong>  </li>
<li>그래서 <code>haru.age++</code> 하면 <code>haru2.age</code>도 같이 바뀝니다.  <pre><code class="language-js">const haru2 = haru;
haru.age++; // haru2.age도 같이 증가함</code></pre>
</li>
</ul>
<h4 id="objectassign-haru-또는--haru-"><code>Object.assign({}, haru)</code> 또는 <code>{ ...haru }</code></h4>
<ul>
<li><strong><u>얕은 복사(shallow copy)</u></strong>  </li>
<li>)</li>
<li><code>haru</code> 객체의 <strong>최상위 속성</strong>만 새 객체로 복사 </li>
<li><strong>중첩 객체</strong>가 있다면 그 안은 <strong>참조로 복사</strong></li>
</ul>
<p>예시:</p>
<pre><code class="language-js">const haru = { name: &#39;하루&#39;, age: 5, pet: { type: &#39;dog&#39; } };
const copy = { ...haru };
copy.pet.type = &#39;cat&#39;;
console.log(haru.pet.type); // &#39;cat&#39; (내부 객체는 공유됨)</code></pre>
<hr>
<p>정리하면:</p>
<table>
<thead>
<tr>
<th>복사 방법</th>
<th>복사 종류</th>
<th>중첩 객체까지 복사?</th>
</tr>
</thead>
<tbody><tr>
<td><code>haru2 = haru</code></td>
<td>참조 복사</td>
<td>X (같은 객체 공유)</td>
</tr>
<tr>
<td><code>Object.assign({}, haru)</code></td>
<td>얕은 복사</td>
<td>X</td>
</tr>
<tr>
<td><code>{ ...haru }</code></td>
<td>얕은 복사</td>
<td>X</td>
</tr>
<tr>
<td><code>JSON.parse(JSON.stringify())</code></td>
<td>깊은 복사</td>
<td>O (단점 있음)</td>
</tr>
<tr>
<td><code>lodash.cloneDeep()</code></td>
<td>깊은 복사</td>
<td>O</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋사] js-unknown type]]></title>
            <link>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-js-unknown-type</link>
            <guid>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-js-unknown-type</guid>
            <pubDate>Mon, 28 Apr 2025 01:47:51 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/bab_shunn/post/b36b003d-4f8a-4bf6-a74b-e09ff0bdbe35/image.png" alt=""></p>
<h2 id="unknown-type">unknown type</h2>
<p><code>TypeScript 3.0</code> 부터 새로 추가된 타입
문자 그대로 <code>알려지지 않은 타입</code> 이다.
unknown 타입으로 선언된 변수의 값으로는 모든 타입이 올 수 있다. (any타입과 비슷)
그렇다면 <code>unknown 과 any 의 차이점</code>은 무엇일까?</p>
<h3 id="unknown-과-any의-차이">unknown 과 any의 차이</h3>
<blockquote>
<p>unknown에는 메서드를 호출할 수 없다.</p>
</blockquote>
<p>어떤 타입인지 확실하지 않기 때문에 타입스크립트는 어떠한 연산이든 실행하지 않는다.</p>
<pre><code class="language-js">let userInput: unknown;
console.log(userInput.length); // ❌ 에러</code></pre>
<p>unknown 타입을 가진 변수는 any를 제외하고 다른 타입을 가진 변수에 할당할 수 없다.
다시 말해, <u>고정된 타입을 가지고 있는 변수에 unknown 타입을 가진 변수를 할당할 수 없다.</u></p>
<pre><code class="language-js">let userInput: unknown;
let userName: string;

userInput = 5; // ⭕️ 작동
userInput = &#39;Harry&#39;; // ⭕️ 작동
userName = userInput; // ❌ 에러</code></pre>
<blockquote>
<p>unknown 타입을 가진 변수를 다른 변수에 할당하기 위해서는 타입 체크를 해주면 된다.</p>
</blockquote>
<pre><code class="language-js">let userInput: unknown;
let userName: string;

userInput = 5;
userInput = &#39;Harry&#39;;
if (typeof userInput === &#39;string&#39;) { 
  userName = userInput; // ⭕️ 작동
}</code></pre>
<h3 id="unknown-타입을-쓰는-이유">unknown 타입을 쓰는 이유</h3>
<p><strong>변수의 값으로 어떤 타입이 올지 미정인 상태에서 any를 쓴다면 타입스크립트를 사용하는 이유가 없다.</strong>(typescipt: 그럴거면 js로 돌아가.)</p>
<p>이럴 때 any 보다 깐깐한(?) unknown 타입을 사용하는 것이 좋다. (타입 선언 이후 조건문 등으로 타입 체크 코드 작성하면 unknown 타입을 잘 사용할 수 있다!)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋사] js-var사용으로 인해 초래될 수 있는 문제]]></title>
            <link>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-js-var%EC%82%AC%EC%9A%A9%EC%9C%BC%EB%A1%9C-%EC%9D%B8%ED%95%B4-%EC%B4%88%EB%9E%98%EB%90%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@bab_shunn/%EB%A9%8B%EC%82%AC-js-var%EC%82%AC%EC%9A%A9%EC%9C%BC%EB%A1%9C-%EC%9D%B8%ED%95%B4-%EC%B4%88%EB%9E%98%EB%90%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Fri, 25 Apr 2025 04:48:52 GMT</pubDate>
            <description><![CDATA[<h3 id="var은-함수-스코프를-가진다-이것이-어떤-문제를-초래할-수-있는지-알아보자">&quot;<em>var은 함수 스코프를 가진다. 이것이 어떤 문제를 초래할 수 있는지 알아보자.</em>&quot;</h3>
<h3 id="요구사항">요구사항</h3>
<blockquote>
<p><strong>문서내의 모든 버튼을 클릭했을때</strong>,
각각 &quot;btn-0 클릭&quot;, &quot;btn-1 클릭&quot;, &quot;btn-2 클릭&quot;
... &quot;btn-n 클릭&quot;을 콘솔에 출력하시오.</p>
</blockquote>
<pre><code class="language-js">&lt;script&gt;
      window.onload = function () {
        const btns = document.querySelectorAll(&quot;button&quot;);
        // IIFE pattern
        for (var i = 0; i &lt; btns.length; i++) {
          btns[i].addEventListener(&quot;click&quot;, function () {
            console.log(`btn-${num}클릭`);
          });
        }
      };
    &lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/b40ecfe6-56af-456c-ba97-feac936aea67/image.png" alt="">
위의 그림에서 알수 있듯이 var을 사용하면 원하는 결과를 얻을 수 없다.</p>
<h3 id="왜-그런걸까">왜 그런걸까?</h3>
<ol>
<li><p>i = 0일 때, function() { console.log(i); } 함수를 배열에 저장.</p>
</li>
<li><p>i = 1일 때도 또 함수 저장.</p>
</li>
<li><p>i = 2일 때도 또 함수 저장.</p>
</li>
</ol>
<p>그래서 funcs에는 함수가 3개 저장:</p>
<pre><code class="language-js">[
  function() { console.log(i); },
  function() { console.log(i); },
  function() { console.log(i); }
]</code></pre>
<blockquote>
<p>이 세 함수는 모두 같은 i를 바라보고 있다! (어매나)</p>
</blockquote>
<blockquote>
<p>현재 위 코드에서 i는 모든 함수들이 같이 보는 공용 메모장 같은 것
반복문은 메모장에 숫자 적으면서 함수들을 저장해두는 거고,
함수들은 <strong>&quot;나중에 저 메모장에 적힌 거 보고 출력할게!&quot;</strong> 라고 약속해놓은 상황.
근데 반복문이 끝나고 나면, 메모장에는 3만 써 있음. <del>(var라는 놈이 이래서 문제다,,)</del>
그래서 함수들이 실행될 때마다 3만 출력</p>
</blockquote>
<pre><code class="language-js">&lt;script&gt;
      window.onload = function () {
        // 문서내의 모든 버튼을 클릭했을때
        // 각각 &quot;btn-0 클릭&quot;, &quot;btn-1 클릭&quot;, &quot;btn-2 클릭&quot;
        // ... &quot;btn-n 클릭&quot;을 콘솔에 출력하시오.
        const btns = document.querySelectorAll(&quot;button&quot;);
        // IIFE pattern
        for (var i = 0; i &lt; btns.length; i++) {
          ((num) =&gt; {
            btns[i].addEventListener(&quot;click&quot;, function () {
              console.log(`btn-${num}클릭`);
            });
          })(i);
        }
      };
    &lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/2e7fa1d1-9fac-42db-97e6-ef56e0af0c7e/image.png" alt="">
이런 방식으로 해결할 수 있다.</p>
<p>하지만 지금은 2025년. </p>
<h3 id="ecma6의-시대">ECMA6의 시대.</h3>
<p><u>우리는 let과 forEach를 사용함으로써 이를 해결할 수 있다.</u></p>
<pre><code class="language-js">    // ECMA5 forEach
    // Array.prototype.forEach: 배열의 각 요소를 순회하면서 지정한 콜백 함수를 실행
    // 콜백 함수는 첫번째 인자로 배열의 요소, 두번째 인자로 요소의 인덱스를 전달받는다.
    // [btn1, btn2, btn3].forEach((btn, i) =&gt; { });
    btns.forEach(function(btn, i){
      btn.addEventListener(&#39;click&#39;, function(){
        console.log(`btn-${i} 클릭`);
      });
    });</code></pre>
<pre><code class="language-js">    // ECMA6 let
    for(let i=0; i&lt;btns.length; i++){
      btns[i].addEventListener(&#39;click&#39;, function(){
        console.log(`btn-${i} 클릭`);
      });
        }</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[7. Event]]></title>
            <link>https://velog.io/@bab_shunn/7.-Event</link>
            <guid>https://velog.io/@bab_shunn/7.-Event</guid>
            <pubDate>Thu, 17 Apr 2025 15:55:04 GMT</pubDate>
            <description><![CDATA[<h2 id="1-introduction">1. Introduction</h2>
<p>이벤트(event)는 어떤 사건을 의미한다. 브라우저에서의 이벤트란 예를 들어 사용자가 버튼을 클릭했을 때, 웹페이지가 로드되었을 때와 같은 것인데 이것은 DOM 요소와 관련이 있다.</p>
<p>이벤트가 발생하는 시점이나 순서를 사전에 인지할 수 없으므로 일반적인 제어 흐름과는 다른 접근 방식이 필요하다. <u><strong>즉, 이벤트가 발생하면 누군가 이를 감지할 수 있어야 하며 그에 대응하는 처리를 호출해 주어야 한다.</strong></u></p>
<p>브라우저는 이벤트를 감지할 수 있으며 이벤트 발생 시에는 통지해 준다. 이 과정을 통해 사용자와 웹페이지는 상호작용(Interaction)이 가능하게 된다.</p>
<pre><code class="language-js">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;body&gt;
  &lt;button class=&quot;myButton&quot;&gt;Click me!&lt;/button&gt;
  &lt;script&gt;
    document.querySelector(&#39;.myButton&#39;).addEventListener(&#39;click&#39;, function () {
      alert(&#39;Clicked!&#39;);
    });
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>이벤트가 발생하면 그에 맞는 반응을 하여야 한다. 이를 위해 <u><strong>이벤트는 일반적으로 함수에 연결되며 그 함수는 이벤트가 발생하기 전에는 실행되지 않다가 이벤트가 발생되면 실행된다.</strong> 이 함수를 이벤트 핸들러라 하며 이벤트에 대응하는 처리를 기술한다.</u></p>
<h2 id="2-이벤트-루프event-loop와-동시성concurrency">2. 이벤트 루프(Event Loop)와 동시성(Concurrency)</h2>
<p>브라우저는 <strong>단일 쓰레드(single-thread)</strong>에서 이벤트 드리븐(event-driven) 방식으로 동작한다.</p>
<p>단일 쓰레드는 쓰레드가 하나뿐이라는 의미이며 이말은 곧 하나의 작업(task)만을 처리할 수 있다는 것을 의미한다. 하지만 실제로 동작하는 웹 애플리케이션은 많은 task가 동시에 처리되는 것처럼 느껴진다. <strong>이처럼 자바스크립트의 동시성(Concurrency)을 지원하는 것이 바로 이벤트 루프(Event Loop)이다.</strong></p>
<p>브라우저의 환경을 그림으로 표현하면 아래와 같다.
<img src="https://velog.velcdn.com/images/bab_shunn/post/6ef7dbcd-5680-406f-88be-5d0cddc42f71/image.png" alt="event-loop">
이벤트 루프(Event Loop)와 브라우저의 환경</p>
<p>구글의 V8을 비롯한 대부분의 자바스크립트 엔진은 크게 2개의 영역으로 나뉜다.</p>
<blockquote>
<p><strong>Call Stack(호출 스택)</strong>
작업이 요청되면(함수가 호출되면) 요청된 작업은 순차적으로 Call Stack에 쌓이게 되고 순차적으로 실행된다. 자바스크립트는 단 하나의 Call Stack을 사용하기 때문에 해당 task가 종료하기 전까지는 다른 어떤 task도 수행될 수 없다.</p>
</blockquote>
<blockquote>
<p><strong>Heap</strong>
동적으로 생성된 객체 인스턴스가 할당되는 영역이다.</p>
</blockquote>
<p>이와 같이 자바스크립트 엔진은 단순히 작업이 요청되면 Call Stack을 사용하여 요청된 작업을 순차적으로 실행할 뿐이다. 앞에서 언급한 동시성(Concurrency)을 지원하기 위해 필요한 비동기 요청(이벤트를 포함) 처리는 자바스크립트 엔진을 구동하는 환경 즉 브라우저(또는 Node.js)가 담당한다.</p>
<p>Event Queue(Task Queue)
비동기 처리 함수의 콜백 함수, 비동기식 이벤트 핸들러, Timer 함수(setTimeout(), setInterval())의 콜백 함수가 보관되는 영역으로 이벤트 루프(Event Loop)에 의해 특정 시점(Call Stack이 비어졌을 때)에 순차적으로 Call Stack으로 이동되어 실행된다.
Event Loop(이벤트 루프)
Call Stack 내에서 현재 실행중인 task가 있는지 그리고 Event Queue에 task가 있는지 반복하여 확인한다. 만약 Call Stack이 비어있다면 Event Queue 내의 task가 Call Stack으로 이동하고 실행된다.
아래의 예제가 어떻게 동작할지 살펴보자.</p>
<p>function func1() {
  console.log(&#39;func1&#39;);
  func2();
}</p>
<p>function func2() {
  setTimeout(function () {
    console.log(&#39;func2&#39;);
  }, 0);</p>
<p>  func3();
}</p>
<p>function func3() {
  console.log(&#39;func3&#39;);
}</p>
<p>func1();
함수 func1이 호출되면 함수 func1은 Call Stack에 쌓인다. 그리고 함수 func1은 함수 func2을 호출하므로 함수 func2가 Call Stack에 쌓이고 setTimeout가 호출된다. setTimeout의 콜백함수는 즉시 실행되지 않고 지정 대기 시간만큼 기다리다가 “tick” 이벤트가 발생하면 태스크 큐로 이동한 후 Call Stack이 비어졌을 때 Call Stack으로 이동되어 실행된다.</p>
<p>event-loop</p>
<p>이벤트 루프(Event Loop)에 의한 setTimeout 콜백함수의 실행</p>
<p>DOM 이벤트 핸들러도 이와 같이 동작한다.</p>
<p>function func1() {
  console.log(&#39;func1&#39;);
  func2();
}</p>
<p>function func2() {
  // <button class="foo">foo</button>
  const elem = document.querySelector(&#39;.foo&#39;);</p>
<p>  elem.addEventListener(&#39;click&#39;, function () {
    this.style.backgroundColor = &#39;indigo&#39;;
    console.log(&#39;func2&#39;);
  });</p>
<p>  func3();
}</p>
<p>function func3() {
  console.log(&#39;func3&#39;);
}</p>
<p>func1();
함수 func1이 호출되면 함수 func1은 Call Stack에 쌓인다. 그리고 함수 func1은 함수 func2을 호출하므로 함수 func2가 Call Stack에 쌓이고 addEventListener가 호출된다. addEventListener의 콜백함수는 foo 버튼이 클릭되어 click 이벤트가 발생하면 태스크 큐로 이동한 후 Call Stack이 비어졌을 때 Call Stack으로 이동되어 실행된다.</p>
<p>#3. 이벤트의 종류
대표적인 이벤트를 소개한다. 자세한 사항은 Event reference을 참조 바란다.</p>
<p>#3.1 UI Event
Event    Description
load    웹페이지의 로드가 완료되었을 때
unload    웹페이지가 언로드될 때(주로 새로운 페이지를 요청한 경우)
error    브라우저가 자바스크립트 오류를 만났거나 요청한 자원이 존재하지 않는 경우
resize    브라우저 창의 크기를 조절했을 때
scroll    사용자가 페이지를 위아래로 스크롤할 때
select    텍스트를 선택했을 때
#3.2 Keyboard Event
Event    Description
keydown    키를 누르고 있을 때
keyup    누르고 있던 키를 뗄 때
keypress    키를 누르고 뗏을 때
#3.3 Mouse Event
Event    Description
click    마우스 버튼을 클릭했을 때
dbclick    마우스 버튼을 더블 클릭했을 때
mousedown    마우스 버튼을 누르고 있을 때
mouseup    누르고 있던 마우스 버튼을 뗄 때
mousemove    마우스를 움직일 때 (터치스크린에서 동작하지 않는다)
mouseover    마우스를 요소 위로 움직였을 때 (터치스크린에서 동작하지 않는다)
mouseout    마우스를 요소 밖으로 움직였을 때 (터치스크린에서 동작하지 않는다)
#3.4 Focus Event
Event    Description
focus/focusin    요소가 포커스를 얻었을 때
blur/foucusout    요소가 포커스를 잃었을 때
#3.5 Form Event
Event    Description
input    input 또는 textarea 요소의 값이 변경되었을 때
     contenteditable 어트리뷰트를 가진 요소의 값이 변경되었을 때
change    select box, checkbox, radio button의 상태가 변경되었을 때
submit    form을 submit할 때 (버튼 또는 키)
reset    reset 버튼을 클릭할 때 (최근에는 사용 안함)
#3.6 Clipboard Event
Event    Description
cut    콘텐츠를 잘라내기할 때
copy    콘텐츠를 복사할 때
paste    콘텐츠를 붙여넣기할 때
#4. 이벤트 핸들러 등록
이벤트가 발생했을 때 동작할 이벤트 핸들러를 이벤트에 등록하는 방법은 아래와 같이 3가지이다.</p>
<p>#4.1 인라인 이벤트 핸들러 방식
HTML 요소의 이벤트 핸들러 어트리뷰트에 이벤트 핸들러를 등록하는 방법이다.</p>
<!DOCTYPE html>
<html>
<body>
  <button onclick="myHandler()">Click me</button>
  <script>
    function myHandler() {
      alert('Button clicked!');
    }
  </script>
</body>
</html>

<p>이 방식은 더 이상 사용되지 않으며 사용해서도 않된다. 오래된 코드에서 간혹 이 방식을 사용한 것이 있기 때문에 알아둘 필요는 있다. HTML과 Javascript는 관심사가 다르므로 분리하는 것이 좋다.</p>
<p>최근 관심을 받고 있는 CBD(Component Based Development) 방식의 Angular/React/Vue.js와 같은 프레임워크/라이브러리에서는 인라인 이벤트 핸들러 방식으로 이벤트를 처리한다. CBD에서는 HTML, CSS, 자바스크립트를 뷰를 구성하기 위한 구성 요소로 보기 때문에 관심사가 다르다고 생각하지 않는다.</p>
<p>주의할 것은 onclick과 같이 on으로 시작하는 이벤트 어트리뷰트의 값으로 함수 호출을 전달한다는 것이다. 다음에 살펴볼 이벤트 핸들러 프로퍼티 방식에는 DOM 요소의 이벤트 핸들러 프로퍼티에 함수 호출이 아닌 함수를 전달한다.</p>
<p>이때 이벤트 어트리뷰트의 값으로 전달한 함수 호출이 즉시 호출되는 것은 아니다. 사실은 이벤트 어트리뷰트 키를 이름으로 갖는 함수를 암묵적으로 정의하고 그 함수의 몸체에 이벤트 어트리뷰트의 값으로 전달한 함수 호출을 문으로 갖는다. 위 예제의 경우, button 요소의 onclick 프로퍼티에 함수 function onclick(event) { foo(); }가 할당된다.</p>
<p>즉, 이벤트 어트리뷰트의 값은 암묵적으로 정의되는 이벤트 핸들러의 문이다. 따라서 아래와 같이 여러 개의 문을 전달할 수 있다.</p>
<!DOCTYPE html>
<html>
<body>
  <button onclick="myHandler1(); myHandler2();">Click me</button>
  <script>
    function myHandler1() {
      alert('myHandler1');
    }
    function myHandler2() {
      alert('myHandler2');
    }
  </script>
</body>
</html>

<p>#4.2 이벤트 핸들러 프로퍼티 방식
인라인 이벤트 핸들러 방식처럼 HTML과 Javascript가 뒤섞이는 문제는 해결할 수 있는 방식이다. 하지만 이벤트 핸들러 프로퍼티에 하나의 이벤트 핸들러만을 바인딩할 수 있다는 단점이 있다.</p>
<!DOCTYPE html>
<html>
<body>
  <button class="btn">Click me</button>
  <script>
    const btn = document.querySelector('.btn');

<pre><code>// 이벤트 핸들러 프로퍼티 방식은 이벤트에 하나의 이벤트 핸들러만을 바인딩할 수 있다
// 첫번째 바인딩된 이벤트 핸들러 =&gt; 실행되지 않는다.
btn.onclick = function () {
  alert(&#39;① Button clicked 1&#39;);
};

// 두번째 바인딩된 이벤트 핸들러
btn.onclick = function () {
  alert(&#39;① Button clicked 2&#39;);
};

// addEventListener 메소드 방식
// 첫번째 바인딩된 이벤트 핸들러
btn.addEventListener(&#39;click&#39;, function () {
  alert(&#39;② Button clicked 1&#39;);
});

// 두번째 바인딩된 이벤트 핸들러
btn.addEventListener(&#39;click&#39;, function () {
  alert(&#39;② Button clicked 2&#39;);
});</code></pre><p>  </script></p>
</body>
</html>

<p>#4.3 addEventListener 메소드 방식
addEventListener 메소드를 이용하여 대상 DOM 요소에 이벤트를 바인딩하고 해당 이벤트가 발생했을 때 실행될 콜백 함수(이벤트 핸들러)를 지정한다.</p>
<p>Event Listener</p>
<p>addEventListener 메소드</p>
<p>addEventListener 함수 방식은 이전 방식에 비해 아래와 같이 보다 나은 장점을 갖는다.</p>
<p>하나의 이벤트에 대해 하나 이상의 이벤트 핸들러를 추가할 수 있다.
캡처링과 버블링을 지원한다.
HTML 요소뿐만아니라 모든 DOM 요소(HTML, XML, SVG)에 대해 동작한다. 브라우저는 웹 문서(HTML, XML, SVG)를 로드한 후, 파싱하여 DOM을 생성한다.
addEventListener 메소드는 IE 9 이상에서 동작한다. IE 8 이하에서는 attachEvent 메소드를 사용한다.</p>
<p>if (elem.addEventListener) {    // IE 9 ~
  elem.addEventListener(&#39;click&#39;, func);
} else if (elem.attachEvent) {  // ~ IE 8
  elem.attachEvent(&#39;onclick&#39;, func);
}
addEventListener 메소드의 사용 예제를 살펴보자.</p>
<!DOCTYPE html>
<html>
<body>
  <script>
    addEventListener('click', function () {
      alert('Clicked!');
    });
  </script>
</body>
</html>

<p>위와 같이 대상 DOM 요소(target)를 지정하지 않으면 전역객체 window, 즉 DOM 문서를 포함한 브라우저의 윈도우에서 발생하는 click 이벤트에 이벤트 핸들러를 바인딩한다. 따라서 브라우저 윈도우 어디를 클릭하여도 이벤트 핸들러가 동작한다.</p>
<!DOCTYPE html>
<html>
<body>
  <label>User name <input type='text'></label>

  <script>
    const input = document.querySelector('input[type=text]');

    input.addEventListener('blur', function () {
      alert('blur event occurred!');
    });
  </script>
</body>
</html>

<p>위 예제는 input 요소에서 발생하는 blur 이벤트에 이벤트 핸들러를 바인딩하였다. 사용자 이름이 최소 2자 이상이야한다는 규칙을 세우고 이에 부합하는지 확인해보자.</p>
<!DOCTYPE html>
<html>
<body>
  <label>User name <input type='text'></label>
  <em class="message"></em>

  <script>
    const input = document.querySelector('input[type=text]');
    const msg = document.querySelector('.message');

    input.addEventListener('blur', function () {
      if (input.value.length < 2) {
        msg.innerHTML = '이름은 2자 이상 입력해 주세요';
      } else {
        msg.innerHTML = '';
      }
    });
  </script>
</body>
</html>

<p>2자 이상이라는 규칙이 바뀌면 이 규칙을 확인하는 모든 코드를 수정해야 한다. 따라서 이러한 방식의 코딩은 바람직하지 않다. 이유는 규모가 큰 프로그램의 경우 수정과 테스트에 소요되는 자원의 낭비도 문제이지만 수정에는 거의 대부분 실수가 동반되기 때문이다.</p>
<p>2자 이상이라는 규칙을 상수화하고 함수의 인수로 전달도록 수정하자. 이렇게 하면 규칙이 변경되어도 함수는 수정하지 않아도 된다.</p>
<p>그런데 addEventListener 메소드의 두번째 매개변수는 이벤트가 발생했을 때 호출될 이벤트 핸들러이다. 이때 두번째 매개변수에는 함수 호출이 아니라 함수 자체를 지정하여야 한다.</p>
<p>function foo() {
  alert(&#39;clicked!&#39;);
}
// elem.addEventListener(&#39;click&#39;, foo()); // 이벤트 발생 시까지 대기하지 않고 바로 실행된다
elem.addEventListener(&#39;click&#39;, foo);      // 이벤트 발생 시까지 대기한다
따라서 이벤트 핸들러 프로퍼티 방식과 같이 이벤트 핸들러 함수에 인수를 전달할 수 없는 문제가 발생한다. 이를 우회하는 방법은 아래와 같다.</p>
<!DOCTYPE html>
<html>
<body>
  <label>User name <input type='text'></label>
  <em class="message"></em>

  <script>
    const MIN_USER_NAME_LENGTH = 2; // 이름 최소 길이

    const input = document.querySelector('input[type=text]');
    const msg = document.querySelector('.message');

    function checkUserNameLength(n) {
      if (input.value.length < n) {
        msg.innerHTML = '이름은 ' + n + '자 이상이어야 합니다';
      } else {
        msg.innerHTML = '';
      }
    }

    input.addEventListener('blur', function () {
      // 이벤트 핸들러 내부에서 함수를 호출하면서 인수를 전달한다.
      checkUserNameLength(MIN_USER_NAME_LENGTH);
    });

    // 이벤트 핸들러 프로퍼티 방식도 동일한 방식으로 인수를 전달할 수 있다.
    // input.onblur = function () {
    //   // 이벤트 핸들러 내부에서 함수를 호출하면서 인수를 전달한다.
    //   checkUserNameLength(MIN_USER_NAME_LENGTH);
    // };
  </script>
</body>
</html>

<p>#5. 이벤트 핸들러 함수 내부의 this
#5.1 인라인 이벤트 핸들러 방식
인라인 이벤트 핸들러 방식의 경우, 이벤트 핸들러는 일반 함수로서 호출되므로 이벤트 핸들러 내부의 this는 전역 객체 window를 가리킨다.</p>
<!DOCTYPE html>
<html>
<body>
  <button onclick="foo()">Button</button>
  <script>
    function foo () {
      console.log(this); // window
    }
  </script>
</body>
</html>
#5.2 이벤트 핸들러 프로퍼티 방식
이벤트 핸들러 프로퍼티 방식에서 이벤트 핸들러는 메소드이므로 이벤트 핸들러 내부의 this는 이벤트에 바인딩된 요소를 가리킨다. 이것은 이벤트 객체의 currentTarget 프로퍼티와 같다.

<!DOCTYPE html>
<html>
<body>
  <button class="btn">Button</button>
  <script>
    const btn = document.querySelector('.btn');

<pre><code>btn.onclick = function (e) {
  console.log(this); // &lt;button id=&quot;btn&quot;&gt;Button&lt;/button&gt;
  console.log(e.currentTarget); // &lt;button id=&quot;btn&quot;&gt;Button&lt;/button&gt;
  console.log(this === e.currentTarget); // true
};</code></pre><p>  </script></p>
</body>
</html>
#5.3 addEventListener 메소드 방식
addEventListener 메소드에서 지정한 이벤트 핸들러는 콜백 함수이지만 이벤트 핸들러 내부의 this는 이벤트 리스너에 바인딩된 요소(currentTarget)를 가리킨다. 이것은 이벤트 객체의 currentTarget 프로퍼티와 같다.

<!DOCTYPE html>
<html>
<body>
  <button class="btn">Button</button>
  <script>
    const btn = document.querySelector('.btn');

<pre><code>btn.addEventListener(&#39;click&#39;, function (e) {
  console.log(this); // &lt;button id=&quot;btn&quot;&gt;Button&lt;/button&gt;
  console.log(e.currentTarget); // &lt;button id=&quot;btn&quot;&gt;Button&lt;/button&gt;
  console.log(this === e.currentTarget); // true
});</code></pre><p>  </script></p>
</body>
</html>
#6. 이벤트의 흐름
계층적 구조에 포함되어 있는 HTML 요소에 이벤트가 발생할 경우 연쇄적 반응이 일어난다. 즉, 이벤트가 전파(Event Propagation)되는데 전파 방향에 따라 버블링(Event Bubbling)과 캡처링(Event Capturing)으로 구분할 수 있다.

<p>자식 요소에서 발생한 이벤트가 부모 요소로 전파되는 것을 버블링이라 하고, 자식 요소에서 발생한 이벤트가 부모 요소부터 시작하여 이벤트를 발생시킨 자식 요소까지 도달하는 것을 캡처링이라 한다. 주의할 것은 버블링과 캡처링은 둘 중에 하나만 발생하는 것이 아니라 캡처링부터 시작하여 버블링으로 종료한다는 것이다. 즉, 이벤트가 발생했을 때 캡처링과 버블링은 순차적으로 발생한다.</p>
<p>캡처링은 IE8 이하에서 지원되지 않는다.</p>
<p>event flow</p>
<p><a href="http://www.w3.org/TR/DOM-Level-3-Events">www.w3.org/TR/DOM-Level-3-Events</a></p>
<p>addEventListener 메소드의 세번째 매개변수에 true를 설정하면 캡처링으로 전파되는 이벤트를 캐치하고 false 또는 미설정하면 버블링으로 전파되는 이벤트를 캐치한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[6. window/document에서 발생하는 이벤트?]]></title>
            <link>https://velog.io/@bab_shunn/6.-windowdocument%EC%97%90%EC%84%9C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EC%9D%B4%EB%B2%A4%ED%8A%B8</link>
            <guid>https://velog.io/@bab_shunn/6.-windowdocument%EC%97%90%EC%84%9C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EC%9D%B4%EB%B2%A4%ED%8A%B8</guid>
            <pubDate>Wed, 16 Apr 2025 00:46:43 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/bab_shunn/post/02625c5b-2b40-4af4-ad7d-86f599003067/image.png" alt=""></p>
<pre><code class="language-js">      //HTML 문서 로딩 완료, 이미지, CSS 같은 외부 컨텐츠 로딩 완료 후에 발생하는 이벤틏
      window.addEventListener(&quot;load&quot;, function () {});

      //HTML 문서 로딩 완료(DOM 객체 생성 완료) 후에 발생하는 이벤트
      document.addEventListener(&quot;DOMContentLoaded&quot;, function () {};</code></pre>
<blockquote>
<p>자바스크립트 코드를 짜며 window객체와 document객체의 차이에 대해 잘 알지 못하고 사용한 점이 생각나서 포스팅한 게시물입니다.</p>
</blockquote>
<p>평소에 window / document 객체를 참조하는 흔한 작업들은 다음과 같았다.</p>
<ol>
<li>이벤트 리스너 등록</li>
</ol>
<pre><code class="language-js">window.addEventListener(&quot;scroll&quot;, function () {});
document.addEventListener(&quot;click&quot;, function() {});</code></pre>
<ol start="2">
<li>dom에 접근</li>
</ol>
<pre><code class="language-js">document.body.style.backgroundColor = &quot;#fafafc&quot;;
document.getElementById(&quot;nextForm&quot;).focus();</code></pre>
<ol start="3">
<li>스크롤 위치 이동</li>
</ol>
<pre><code class="language-js">window.scrollTo(pos);</code></pre>
<ol start="4">
<li>창 닫기 / 열기</li>
</ol>
<pre><code class="language-js">window.close();
window.open(url);</code></pre>
<p>나열해놓고 보니 window객체와 document객체에서 하는 일이 뭔가 미묘하게 다른 건 알겠다.</p>
<p>하지만 여전히 명확한 차이가 무엇인지, 두 객체의 addEventListener가 어떤 차이인지 모르겠다.</p>
<p>다행히 이러한 궁금증을 나만 가진것이 아니었기 때문에 다른 분들이 stackoverflow에 작성하신 질문들이 있었고, 거기엔 좋은 답변들이 준비되어 있었다.</p>
<p><a href="https://stackoverflow.com/questions/9895202/what-is-the-difference-between-window-screen-and-document">https://stackoverflow.com/questions/9895202/what-is-the-difference-between-window-screen-and-document</a></p>
<p><a href="https://stackoverflow.com/questions/12045440/difference-between-document-addeventlistener-and-window-addeventlistener">https://stackoverflow.com/questions/12045440/difference-between-document-addeventlistener-and-window-addeventlistener</a></p>
<p>결론만 요약하자면 다음과 같다.</p>
<ul>
<li><p><strong>window 객체는 브라우저라는 host 환경에서 동작하는 최상위 객체</strong>로, 브라우저 환경에서 동작하는 여러 API들의 가교 역할을 한다. document, history, location, setTimout, cookieStore 등 다른 브라우저 api에 접근을 제공한다.</p>
</li>
<li><p><strong>JSDom등의 환경에서는 표준 window 객체의 인터페이스를 바탕으로 구현한 더미 window 객체(더미라고 해도 몇몇 기능들은 동작한다)를 사용</strong>하게 되는데 이 경우에는 실제 브라우저의 인터페이스와 다를 수 있으니 주의해야 한다.</p>
</li>
<li><p><strong>document 객체는 window에 로드되는 HTML 문서 그 자체를 나타내며(document 객체가 존재)</strong>, 해당 문서 내부의 HTML Element, Css 등을 제어할 수 있는 여러 메서드를 제공한다. 보편적으로 window 객체의 프로퍼티로 제공된다. <strong>document 객체는 window.document 혹은 document로 접근이 가능하다.</strong></p>
</li>
<li><p><strong>일반적으로 querySelector 등의 메서드로 css selector 기반의 element 쿼리를 많이 하게 된다.</strong></p>
</li>
<li><p><strong>window 객체는 전역으로 선언되어 있기 때문에 window객체 안에 있는 요소는 &quot;window.&quot;와 같이 window객체를 참조하지 않고도 property 이름으로 바로 접근이 가능하다. 예컨대 window.innerHeight는 그냥 innerHeight로 접근이 가능하다.</strong> ( 오... 신기... 하지만 혼동이나 scope 등의 문제로 window.innerHeight 이런 식으로 사용하는 게 좋을 것 같다. )
document객체와 window객체에서 수용 가능한 eventList가 다르기 때문에, 같은 addEventListener이 있다고 하더라도, 각 용도에 맞게 호출해야 한다.</p>
</li>
</ul>
<h2 id="결론-windowaddeventlister-vs-documentaddeventlister">[결론] <code>window.addEventLister</code> VS <code>document.addEventLister</code></h2>
<blockquote>
<p>사실 둘의 차이는 명확하지 않다. stackoverflow 에 나와 있는 설명에 의하면 각각이 제어하는 이벤트 리스너에 차이가 있기 때문에 구분해서 써야한다고 한다. 대표적으로 resize 이벤트가 있는데 이 이벤트의 경우 window 이벤트 리스너에는 있지만 document 이벤트 리스너에는 있지 않다.</p>
</blockquote>
<p>하지만 click 과 같이 window 와 document 에 공통으로 들어가 있는 이벤트도 있는데 이 경우stackoverflow 의 답변은 이렇다.</p>
<blockquote>
<p><strong>전파된 이벤트의 유일한 주요 차이점은 타이밍</strong>입니다. 이벤트는 계층 구조에서 document 가 window 보다 먼저 발생하지만 일반적으로 그 차이는 중요하지 않으므로 둘 중 하나를 선택해도 됩니다. 일반적으로 전파된 이벤트를 처리할 때는 이벤트의 소스와 가장 가까운 객체를 선택하는 것이 필요에 부합합니다. <strong>즉, 둘 다 작동할 때 window 보다 document 를 선택하는 것이 좋습니다.</strong></p>
</blockquote>
<p>직접 같은 이벤트를 등록하고 이벤트를 실행해보면, document 로 등록한 이벤트가 먼저 실행이 된다.</p>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/0919eb82-500c-4816-baf5-816285504aca/image.png" alt=""></p>
<p>참조: <a href="https://nookpi.tistory.com/56">https://nookpi.tistory.com/56</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[5. DOM 문서 객체 모델(Document Object Model)]]></title>
            <link>https://velog.io/@bab_shunn/DOM-%EB%AC%B8%EC%84%9C-%EA%B0%9D%EC%B2%B4-%EB%AA%A8%EB%8D%B8Document-Object-Model</link>
            <guid>https://velog.io/@bab_shunn/DOM-%EB%AC%B8%EC%84%9C-%EA%B0%9D%EC%B2%B4-%EB%AA%A8%EB%8D%B8Document-Object-Model</guid>
            <pubDate>Tue, 15 Apr 2025 00:06:02 GMT</pubDate>
            <description><![CDATA[<h2 id="1-dom-document-object-model">1. DOM (Document Object Model)</h2>
<p>텍스트 파일로 만들어져 있는 웹 문서를 브라우저에 렌더링하려면<u>** 웹 문서를 브라우저가 이해할 수 있는 구조로 메모리에 올려야 한다.**</u> 브라우저의 렌더링 엔진은 웹 문서를 로드한 후, 파싱하여 웹 문서를 브라우저가 이해할 수 있는 구조로 구성하여 메모리에 적재하는데 이를 DOM이라 한다. </p>
<blockquote>
<p><strong>즉 모든 요소와 요소의 어트리뷰트, 텍스트를 각각의 객체로 만들고 이들 객체를 부자 관계를 표현할 수 있는 트리 구조로 구성한 것이 DOM이다.</strong></p>
</blockquote>
<p>이 DOM은 자바스크립트를 통해 동적으로 변경할 수 있으며 변경된 DOM은 렌더링에 반영된다.</p>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/eb72894e-a9d2-4a0e-a27e-4c88bbd774a9/image.png" alt="브라우저 동작 원리"></p>
<p>브라우저는 웹 문서(HTML, XML, SVG)를 로드한 후, 파싱하여 DOM(문서 객체 모델: Document Object Model)을 생성한다.</p>
<p>이러한 <strong>웹 문서의 동적 변경을 위해 DOM은 프로그래밍 언어가 자신에 접근하고 수정할 수 있는 방법을 제공</strong>하는데 일반적으로 <u><strong>프로퍼티와 메소드를 갖는 자바스크립트 객체로 제공된다. 이를 DOM API(Application Programming Interface)라고 부른다.</strong></u> 달리 말하면 정적인 웹페이지에 접근하여 동적으로 웹페이지를 변경하기 위한 유일한 방법은 메모리 상에 존재하는 DOM을 변경하는 것이고, 이때 필요한 것이 DOM에 접근하고 변경하는 프로퍼티와 메소드의 집합인 DOM API이다.</p>
<p>DOM은 HTML, ECMAScript에서 정의한 표준이 아닌 별개의 W3C의 공식 표준이며 플랫폼/프로그래밍 언어 중립적이다. DOM은 다음 두 가지 기능을 담당한다.</p>
<blockquote>
<p><strong>HTML 문서에 대한 모델 구성</strong>
브라우저는 HTML 문서를 로드한 후 해당 문서에 대한 모델을 메모리에 생성한다. 이때 모델은 객체의 트리로 구성되는데 이것을 DOM tree라 한다.</p>
</blockquote>
<blockquote>
<p><strong>HTML 문서 내의 각 요소에 접근 / 수정</strong>
DOM은 모델 내의 각 객체에 접근하고 수정할 수 있는 프로퍼티와 메소드를 제공한다. DOM이 수정되면 브라우저를 통해 사용자가 보게 될 내용 또한 변경된다.</p>
</blockquote>
<h2 id="2-dom-tree">2. DOM tree</h2>
<p>DOM tree는 브라우저가 HTML 문서를 로드한 후 파싱하여 생성하는 모델을 의미한다. 객체의 트리로 구조화되어 있기 때문에 DOM tree라 부른다.</p>
<pre><code class="language-js">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;style&gt;
      .red  { color: #ff0000; }
      .blue { color: #0000ff; }
    &lt;/style&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div&gt;
      &lt;h1&gt;Cities&lt;/h1&gt;
      &lt;ul&gt;
        &lt;li id=&quot;one&quot; class=&quot;red&quot;&gt;Seoul&lt;/li&gt;
        &lt;li id=&quot;two&quot; class=&quot;red&quot;&gt;London&lt;/li&gt;
        &lt;li id=&quot;three&quot; class=&quot;red&quot;&gt;Newyork&lt;/li&gt;
        &lt;li id=&quot;four&quot;&gt;Tokyo&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/f4b00ac2-134e-4b42-b242-5102fd05c3d0/image.png" alt="DOM tree"></p>
<p>DOM에서 모든 요소, 어트리뷰트, 텍스트는 하나의 객체이며 Document 객체의 자식이다. 요소의 중첩관계는 객체의 트리로 구조화하여 부자관계를 표현한다. DOM tree의 진입점(Entry point)는 document 객체이며 최종점은 요소의 텍스트를 나타내는 객체이다.</p>
<p>DOM tree는 네 종류의 노드로 구성된다.</p>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/0d83392f-01bc-4636-9bdc-0fd44be279dd/image.png" alt=""></p>
<blockquote>
<p><strong>문서 노드(Document Node)</strong>
트리의 최상위에 존재하며 각각 요소, 어트리뷰트, 텍스트 노드에 접근하려면 문서 노드를 통해야 한다. <u>즉, DOM tree에 접근하기 위한 시작점(entry point)이다.</u></p>
</blockquote>
<blockquote>
<p><strong>요소 노드(Element Node)</strong>🔴
요소 노드는 HTML 요소를 표현한다. HTML 요소는 중첩에 의해 부자 관계를 가지며 이 부자 관계를 통해 정보를 구조화한다. <u>따라서 요소 노드는 문서의 구조를 서술한다고 말 할 수 있다.</u> 어트리뷰트, 텍스트 노드에 접근하려면 먼저 요소 노드를 찾아 접근해야 한다. 모든 요소 노드는 요소별 특성을 표현하기 위해 HTMLElement 객체를 상속한 객체로 구성된다. (그림: DOM tree의 객체 구성 참고)</p>
</blockquote>
<blockquote>
<p><strong>어트리뷰트 노드(Attribute Node)</strong>🟡🟢
어트리뷰트 노드는 HTML 요소의 어트리뷰트를 표현한다. <u>어트리뷰트 노드는 해당 어트리뷰트가 지정된 요소의 자식이 아니라 해당 요소의 일부로 표현된다.</u> 따라서 해당 요소 노드를 찾아 접근하면 어트리뷰트를 참조, 수정할 수 있다.</p>
</blockquote>
<blockquote>
<p><strong>텍스트 노드(Text Node)</strong>⚪
텍스트 노드는 HTML 요소의 텍스트를 표현한다. 텍스트 노드는 요소 노드의 자식이며 자신의 자식 노드를 가질 수 없다. 즉, <u>텍스트 노드는 DOM tree의 최종단이다.</u></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/bfdd3ae3-57bc-416f-a247-a90bd940b30b/image.png" alt="Element Node"></p>
<p>DOM tree의 객체 구성</p>
<h2 id="3-dom-query--traversing-요소에의-접근">3. DOM Query / Traversing (요소에의 접근)</h2>
<h3 id="31-하나의-요소-노드-선택dom-query">3.1 하나의 요소 노드 선택(DOM Query)</h3>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/daf08c0b-9f68-482c-8f4f-54e94ab47016/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/bafc49ee-e82e-4e0a-a4de-ebe05b56d0f9/image.png" alt="select an individual element node"></p>
<blockquote>
<p><code>document.getElementById(id)</code></p>
</blockquote>
<ul>
<li><strong>id 어트리뷰트 값</strong>으로 요소 노드를 한 개 선택한다. 복수개가 선택된 경우, 첫번째 요소만 반환한다.</li>
<li>Return: HTMLElement를 상속받은 객체</li>
<li>모든 브라우저에서 동작</li>
</ul>
<pre><code class="language-js">// id로 하나의 요소를 선택한다.
const elem = document.getElementById(&#39;one&#39;);
// 클래스 어트리뷰트의 값을 변경한다.
elem.className = &#39;blue&#39;;

// 그림: DOM tree의 객체 구성 참고
console.log(elem); // &lt;li id=&quot;one&quot; class=&quot;blue&quot;&gt;Seoul&lt;/li&gt;</code></pre>
<blockquote>
<p>⭐<code>document.querySelector(cssSelector)</code></p>
</blockquote>
<ul>
<li><strong>CSS 셀렉터</strong>를 사용하여 요소 노드를 한 개 선택한다. 복수개가 선택된 경우, 첫번째 요소만 반환한다.</li>
<li>Return: HTMLElement를 상속받은 객체</li>
<li>IE8 이상의 브라우저에서 동작</li>
</ul>
<pre><code class="language-js">// CSS 셀렉터를 이용해 요소를 선택한다
const elem = document.querySelector(&#39;li.red&#39;);
// 클래스 어트리뷰트의 값을 변경한다.
elem.className = &#39;blue&#39;;</code></pre>
<h3 id="32-여러-개의-요소-노드-선택dom-query">3.2 여러 개의 요소 노드 선택(DOM Query)</h3>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/e88bc247-2a5f-46f0-8dc9-b3710d34dcac/image.png" alt="select multiful elements"></p>
<blockquote>
<p><code>document.getElementsByClassName(class)</code></p>
</blockquote>
<ul>
<li><strong>class 어트리뷰트 값</strong>으로 요소 노드를 모두 선택한다. 공백으로 구분하여 <strong>여러 개의 class를 지정</strong>할 수 있다.</li>
<li>Return: HTMLCollection (live)</li>
<li>IE9 이상의 브라우저에서 동작</li>
</ul>
<pre><code class="language-js">// HTMLCollection을 반환한다. HTMLCollection은 live하다.
const elems = document.getElementsByClassName(&#39;red&#39;);

for (let i = 0; i &lt; elems.length; i++) {
  // 클래스 어트리뷰트의 값을 변경한다.
  elems[i].className = &#39;blue&#39;;
}</code></pre>
<p>위 예제를 실행해 보면 예상대로 동작하지 않는다. (두번째 요소만 클래스 변경이 되지 않는다.)</p>
<blockquote>
<p><code>document.getElementsByTagName(tagName)</code></p>
</blockquote>
<ul>
<li><strong>태그명</strong>으로 요소 노드를 모두 선택한다.</li>
<li>Return: HTMLCollection (live)</li>
<li>모든 브라우저에서 동작</li>
</ul>
<pre><code class="language-js">// HTMLCollection을 반환한다.
const elems = document.getElementsByTagName(&#39;li&#39;);

[...elems].forEach(elem =&gt; elem.className = &#39;blue&#39;);</code></pre>
<blockquote>
<p><code>document.querySelectorAll(selector)</code></p>
</blockquote>
<ul>
<li>지정된 <strong>CSS 선택자</strong>를 사용하여 <strong>요소 노드를 모두 선택</strong>한다.</li>
<li>Return: NodeList (non-live)</li>
<li>IE8 이상의 브라우저에서 동작</li>
</ul>
<pre><code class="language-js">// Nodelist를 반환한다.
const elems = document.querySelectorAll(&#39;li.red&#39;);

[...elems].forEach(elem =&gt; elem.className = &#39;blue&#39;);</code></pre>
<h3 id="33-dom-traversing-탐색">3.3 DOM Traversing (탐색)</h3>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/2c239b8a-1358-4000-918c-9bb2b913c5aa/image.png" alt="DOM Traversing: nextSibling"></p>
<blockquote>
<p><code>parentNode</code></p>
</blockquote>
<ul>
<li><strong>부모 노드</strong>를 탐색한다.</li>
<li>Return: HTMLElement를 상속받은 객체</li>
<li>모든 브라우저에서 동작</li>
</ul>
<pre><code class="language-js">const elem = document.querySelector(&#39;#two&#39;);

elem.parentNode.className = &#39;blue&#39;;</code></pre>
<blockquote>
<p><code>firstChild</code>, <code>lastChild</code></p>
</blockquote>
<ul>
<li><strong>자식 노드</strong>를 탐색한다.</li>
<li>Return: HTMLElement를 상속받은 객체</li>
<li>IE9 이상의 브라우저에서 동작</li>
</ul>
<pre><code class="language-js">const elem = document.querySelector(&#39;ul&#39;);

// first Child
elem.firstChild.className = &#39;blue&#39;;
// last Child
elem.lastChild.className = &#39;blue&#39;;</code></pre>
<h2 id="4-dom-manipulation-조작">4. DOM Manipulation (조작)</h2>
<h3 id="41-텍스트-노드에의-접근수정">4.1 텍스트 노드에의 접근/수정</h3>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/effacb8e-06c1-4255-a5e7-f42e21f9d927/image.png" alt="nodeValue"></p>
<p>요소의 텍스트는 텍스트 노드에 저장되어 있다. 텍스트 노드에 접근하려면 아래와 같은 수순이 필요하다.</p>
<ol>
<li>해당 텍스트 노드의 부모 노드를 선택한다. 텍스트 노드는 요소 노드의 자식이다.</li>
<li>firstChild 프로퍼티를 사용하여 텍스트 노드를 탐색한다.</li>
<li>텍스트 노드의 유일한 프로퍼티(<code>nodeValue</code>)를 이용하여 텍스트를 취득한다.</li>
<li><code>nodeValue</code>를 이용하여 텍스트를 수정한다.</li>
</ol>
<blockquote>
<p><code>nodeValue</code></p>
</blockquote>
<ul>
<li><strong>노드의 값을 반환</strong>한다.</li>
<li>Return: 텍스트 노드의 경우는 문자열, 요소 노드의 경우 null 반환</li>
<li>IE6 이상의 브라우저에서 동작한다.</li>
</ul>
<p><code>nodeName</code>, <code>nodeType</code>을 통해 노드의 정보를 취득할 수 있다.</p>
<pre><code class="language-js">// 해당 텍스트 노드의 부모 요소 노드를 선택한다.
const one = document.getElementById(&#39;one&#39;);
console.dir(one); // HTMLLIElement: li#one.red

// nodeName, nodeType을 통해 노드의 정보를 취득할 수 있다.
console.log(one.nodeName); // LI
console.log(one.nodeType); // 1: Element node

// firstChild 프로퍼티를 사용하여 텍스트 노드를 탐색한다.
const textNode = one.firstChild;

// nodeName, nodeType을 통해 노드의 정보를 취득할 수 있다.
console.log(textNode.nodeName); // #text
console.log(textNode.nodeType); // 3: Text node

// nodeValue 프로퍼티를 사용하여 노드의 값을 취득한다.
console.log(textNode.nodeValue); // Seoul

// nodeValue 프로퍼티를 이용하여 텍스트를 수정한다.
textNode.nodeValue = &#39;Pusan&#39;;</code></pre>
<h3 id="42-어트리뷰트-노드에의-접근수정">4.2 어트리뷰트 노드에의 접근/수정</h3>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/ffb538a9-52fe-4bbc-ac80-300518294f97/image.png" alt="">
어트리뷰트 노드을 조작할 때 다음 프로퍼티 또는 메소드를 사용할 수 있다.</p>
<blockquote>
<p><code>className</code></p>
</blockquote>
<ul>
<li>class 어트리뷰트의 값을 취득 또는 변경한다. className 프로퍼티에 값을 할당하는 경우, class 어트리뷰트가 존재하지 않으면 class 어트리뷰트를 생성하고 지정된 값을 설정한다. class 어트리뷰트의 값이 여러 개일 경우, 공백으로 구분된 문자열이 반환되므로 String 메소드 <code>split(&#39; &#39;)</code>를 사용하여 배열로 변경하여 사용한다.</li>
<li>모든 브라우저에서 동작한다.</li>
</ul>
<blockquote>
<p><code>classList</code></p>
</blockquote>
<ul>
<li>add, remove, item, toggle, contains, replace 메소드를 제공한다.</li>
<li>IE10 이상의 브라우저에서 동작한다.</li>
</ul>
<pre><code class="language-js">const elems = document.querySelectorAll(&#39;li&#39;);

// className
[...elems].forEach(elem =&gt; {
  // class 어트리뷰트 값을 취득하여 확인
  if (elem.className === &#39;red&#39;) {
    // class 어트리뷰트 값을 변경한다.
    elem.className = &#39;blue&#39;;
  }
});

// classList
[...elems].forEach(elem =&gt; {
  // class 어트리뷰트 값 확인
  if (elem.classList.contains(&#39;blue&#39;)) {
    // class 어트리뷰트 값 변경한다.
    elem.classList.replace(&#39;blue&#39;, &#39;red&#39;);
  }
});</code></pre>
<blockquote>
<p><code>id</code></p>
</blockquote>
<ul>
<li>id 어트리뷰트의 값을 취득 또는 변경한다. id 프로퍼티에 값을 할당하는 경우, id 어트리뷰트가 존재하지 않으면 id 어트리뷰트를 생성하고 지정된 값을 설정한다.</li>
<li>모든 브라우저에서 동작한다.</li>
</ul>
<pre><code class="language-js">// h1 태그 요소 중 첫번째 요소를 취득
const heading = document.querySelector(&#39;h1&#39;);

console.dir(heading); // HTMLHeadingElement: h1
console.log(heading.firstChild.nodeValue); // Cities

// id 어트리뷰트의 값을 변경.
// id 어트리뷰트가 존재하지 않으면 id 어트리뷰트를 생성하고 지정된 값을 설정
heading.id = &#39;heading&#39;;
console.log(heading.id); // heading</code></pre>
<blockquote>
<p><code>hasAttribute(attribute)</code></p>
</blockquote>
<ul>
<li>지정한 어트리뷰트를 가지고 있는지 검사한다.</li>
<li>Return : Boolean</li>
<li>IE8 이상의 브라우저에서 동작한다.</li>
</ul>
<blockquote>
<p><code>getAttribute(attribute)</code></p>
</blockquote>
<ul>
<li>어트리뷰트의 값을 취득한다.</li>
<li>Return : 문자열</li>
<li>모든 브라우저에서 동작한다.</li>
</ul>
<blockquote>
<p><code>setAttribute(attribute, value)</code></p>
</blockquote>
<ul>
<li>어트리뷰트와 어트리뷰트 값을 설정한다.</li>
<li>Return : undefined</li>
<li>모든 브라우저에서 동작한다.</li>
</ul>
<blockquote>
<p><code>removeAttribute(attribute)</code></p>
</blockquote>
<ul>
<li>지정한 어트리뷰트를 제거한다.</li>
<li>Return : undefined</li>
<li>모든 브라우저에서 동작한다.</li>
</ul>
<pre><code class="language-js">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
  &lt;input type=&quot;text&quot;&gt;
  &lt;script&gt;
  const input = document.querySelector(&#39;input[type=text]&#39;);
  console.log(input);

  // value 어트리뷰트가 존재하지 않으면
  if (!input.hasAttribute(&#39;value&#39;)) {
    // value 어트리뷰트를 추가하고 값으로 &#39;hello!&#39;를 설정
    input.setAttribute(&#39;value&#39;, &#39;hello!&#39;);
  }

  // value 어트리뷰트 값을 취득
  console.log(input.getAttribute(&#39;value&#39;)); // hello!

  // value 어트리뷰트를 제거
  input.removeAttribute(&#39;value&#39;);

  // value 어트리뷰트의 존재를 확인
  console.log(input.hasAttribute(&#39;value&#39;)); // false
  &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<pre><code class="language-js">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;body&gt;
  &lt;input class=&quot;password&quot; type=&quot;password&quot; value=&quot;123&quot;&gt;
  &lt;button class=&quot;show&quot;&gt;show&lt;/button&gt;
  &lt;script&gt;
    const $password = document.querySelector(&#39;.password&#39;);
    const $show = document.querySelector(&#39;.show&#39;);

    function makeClickHandler() {
      let isShow = false;
      return function () {
        $password.setAttribute(&#39;type&#39;, isShow ? &#39;password&#39; : &#39;text&#39;);
        isShow = !isShow;
        $show.innerHTML = isShow ? &#39;hide&#39; : &#39;show&#39;;
      };
    }

    $show.onclick = makeClickHandler();
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="43-html-콘텐츠-조작manipulation">4.3 HTML 콘텐츠 조작(Manipulation)</h3>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/b2ceeafa-894a-4fee-824e-17d1bc17e1ee/image.png" alt="innerHTML"></p>
<p>HTML 콘텐츠를 조작(Manipulation)하기 위해 아래의 프로퍼티 또는 메소드를 사용할 수 있다. 마크업이 포함된 콘텐츠를 추가하는 행위는 크로스 스크립팅 공격(XSS: Cross-Site Scripting Attacks)에 취약하므로 주의가 필요하다.</p>
<blockquote>
<p>⭐<code>textContent</code></p>
</blockquote>
<ul>
<li>요소의 텍스트 콘텐츠를 취득 또는 변경한다. 이때 마크업은 무시된다. <strong>textContent를 통해 요소에 새로운 텍스트를 할당하면 텍스트를 변경</strong>할 수 있다. 이때 <strong>순수한 텍스트만 지정</strong>해야 하며 마크업을 포함시키면 문자열로 인식되어 그대로 출력된다.</li>
<li>IE9 이상의 브라우저에서 동작한다.</li>
<li><code>firstChild.nodeValue</code>와 동일</li>
</ul>
<pre><code class="language-js">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;style&gt;
      .red  { color: #ff0000; }
      .blue { color: #0000ff; }
    &lt;/style&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div&gt;
      &lt;h1&gt;Cities&lt;/h1&gt;
      &lt;ul&gt;
        &lt;li id=&quot;one&quot; class=&quot;red&quot;&gt;Seoul&lt;/li&gt;
        &lt;li id=&quot;two&quot; class=&quot;red&quot;&gt;London&lt;/li&gt;
        &lt;li id=&quot;three&quot; class=&quot;red&quot;&gt;Newyork&lt;/li&gt;
        &lt;li id=&quot;four&quot;&gt;Tokyo&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
    &lt;script&gt;
    const ul = document.querySelector(&#39;ul&#39;);

    // 요소의 텍스트 취득
    console.log(ul.textContent);
    /*
            Seoul
            London
            Newyork
            Tokyo
    */

    const one = document.getElementById(&#39;one&#39;);

    // 요소의 텍스트 취득
    console.log(one.textContent); // Seoul

    // 요소의 텍스트 변경
    one.textContent += &#39;, Korea&#39;;

    console.log(one.textContent); // Seoul, Korea

    // 요소의 마크업이 포함된 콘텐츠 변경.
    one.textContent = &#39;&lt;h1&gt;Heading&lt;/h1&gt;&#39;;

    // 마크업이 문자열로 표시된다.
    console.log(one.textContent); // &lt;h1&gt;Heading&lt;/h1&gt;
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
<p>⭐<code>innerHTML</code></p>
</blockquote>
<ul>
<li>해당 요소의 모든 자식 요소를 포함하는 모든 콘텐츠를 하나의 문자열로 취득할 수 있다. 이 문자열은 마크업을 포함한다.</li>
</ul>
<pre><code class="language-js">const ul = document.querySelector(&#39;ul&#39;);

// innerHTML 프로퍼티는 모든 자식 요소를 포함하는 모든 콘텐츠를 하나의 문자열로 취득할 수 있다. 이 문자열은 마크업을 포함한다.
console.log(ul.innerHTML);
// IE를 제외한 대부분의 브라우저들은 요소 사이의 공백 또는 줄바꿈 문자를 텍스트 노드로 취급한다
/*
        &lt;li id=&quot;one&quot; class=&quot;red&quot;&gt;Seoul&lt;/li&gt;
        &lt;li id=&quot;two&quot; class=&quot;red&quot;&gt;London&lt;/li&gt;
        &lt;li id=&quot;three&quot; class=&quot;red&quot;&gt;Newyork&lt;/li&gt;
        &lt;li id=&quot;four&quot;&gt;Tokyo&lt;/li&gt;
*/</code></pre>
<p>innerHTML 프로퍼티를 사용하여 마크업이 포함된 새로운 콘텐츠를 지정하면 새로운 요소를 DOM에 추가할 수 있다.</p>
<pre><code class="language-js">const one = document.getElementById(&#39;one&#39;);

// 마크업이 포함된 콘텐츠 취득
console.log(one.innerHTML); // Seoul

// 마크업이 포함된 콘텐츠 변경
one.innerHTML += &#39;&lt;em class=&quot;blue&quot;&gt;, Korea&lt;/em&gt;&#39;;

// 마크업이 포함된 콘텐츠 취득
console.log(one.innerHTML); // Seoul &lt;em class=&quot;blue&quot;&gt;, Korea&lt;/em&gt;</code></pre>
<p>위와 같이 ⚠️<u><strong>마크업이 포함된 콘텐츠를 추가하는 것은 크로스 스크립팅 공격(XSS: Cross-Site Scripting Attacks)에 취약</strong></u>하다.</p>
<pre><code class="language-js">//솔루션 1: createElement() 메서드 사용
        const newLi = document.createElement(&quot;li&quot;);
        const newTxt = document.createTextNode(&quot;우유&quot;);

        console.log(newLi.childNodes, newLi.firstChild, newLi.lastChild);
        newLi.appendChild(newTxt);
        //shoppingList.appendChild(newLi); //맨 끝에 추가
        shoppingList.insertBefore(newLi, shoppingList.firstChild); //맨 앞에 추가
        /*
         *shoppingList.innerHTML += &quot;&lt;li&gt;우유&lt;/li&gt;&quot;;
         */</code></pre>
<h3 id="44-dom-조작-방식">4.4 DOM 조작 방식</h3>
<p>innerHTML 프로퍼티를 사용하지 않고 새로운 콘텐츠를 추가할 수 있는 방법은 DOM을 직접 조작하는 것이다. 한 개의 요소를 추가하는 경우 사용한다. 이 방법은 다음의 수순에 따라 진행한다.</p>
<ol>
<li><p>요소 노드 생성 <code>createElement()</code> 메소드를 사용하여 새로운 요소 노드를 생성한다. <code>createElement()</code> 메소드의 인자로 <code>태그 이름</code>을 전달한다.</p>
</li>
<li><p>텍스트 노드 생성 <code>createTextNode()</code> 메소드를 사용하여 새로운 텍스트 노드를 생성한다. 경우에 따라 생략될 수 있지만 생략하는 경우, 콘텐츠가 비어 있는 요소가 된다.</p>
</li>
<li><p>생성된 요소를 DOM에 추가 <code>appendChild()</code> 메소드를 사용하여 <strong>생성된 노드를 DOM tree에 추가</strong>한다. 또는 <code>removeChild()</code> 메소드를 사용하여** DOM tree에서 노드를 삭제할 수도 있다.**</p>
</li>
</ol>
<blockquote>
<p><code>createElement(tagName)</code></p>
</blockquote>
<ul>
<li>태그이름을 <strong>인자로 전달하여 요소를 생성</strong>한다.</li>
<li>Return: HTMLElement를 상속받은 객체</li>
<li>모든 브라우저에서 동작한다.</li>
</ul>
<blockquote>
<p><code>createTextNode(text)</code></p>
</blockquote>
<ul>
<li>텍스트를 <strong>인자로 전달하여 텍스트 노드를 생성</strong>한다.</li>
<li>Return: Text 객체</li>
<li>모든 브라우저에서 동작한다.</li>
</ul>
<blockquote>
<p>⭐<code>appendChild(Node)</code></p>
</blockquote>
<ul>
<li><strong>인자로 전달한 노드를 마지막 자식 요소로 DOM 트리에 추가한다</strong>.</li>
<li>Return: 추가한 노드</li>
<li>모든 브라우저에서 동작한다.
<img src="https://velog.velcdn.com/images/bab_shunn/post/cede2e40-ee7e-4462-bce0-4937ad478f98/image.png" alt=""></li>
</ul>
<blockquote>
<p><code>removeChild(Node)</code></p>
</blockquote>
<ul>
<li><strong>인자로 전달한 노드를 DOM 트리에 제거한다.</strong></li>
<li>Return: 추가한 노드</li>
<li>모든 브라우저에서 동작한다.</li>
</ul>
<pre><code>// 태그이름을 인자로 전달하여 요소를 생성
const newElem = document.createElement(&#39;li&#39;);
// const newElem = document.createElement(&#39;&lt;li&gt;test&lt;/li&gt;&#39;);
// Uncaught DOMException: Failed to execute &#39;createElement&#39; on &#39;Document&#39;: The tag name provided (&#39;&lt;li&gt;test&lt;/li&gt;&#39;) is not a valid name.

// 텍스트 노드를 생성
const newText = document.createTextNode(&#39;Beijing&#39;);

// 텍스트 노드를 newElem 자식으로 DOM 트리에 추가
newElem.appendChild(newText);

const container = document.querySelector(&#39;ul&#39;);

// newElem을 container의 자식으로 DOM 트리에 추가. 마지막 요소로 추가된다.
container.appendChild(newElem);

const removeElem = document.getElementById(&#39;one&#39;);

// container의 자식인 removeElem 요소를 DOM 트리에 제거한다.
container.removeChild(removeElem);</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[4. Call by reference가 없는 Javascript]]></title>
            <link>https://velog.io/@bab_shunn/4.-Call-by-reference%EA%B0%80-%EC%97%86%EB%8A%94-Javascript</link>
            <guid>https://velog.io/@bab_shunn/4.-Call-by-reference%EA%B0%80-%EC%97%86%EB%8A%94-Javascript</guid>
            <pubDate>Fri, 11 Apr 2025 05:10:50 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/bab_shunn/post/6224c78b-c947-4649-a29b-dacc5d1292b3/image.png" alt=""></p>
<h2 id="잘못된-생각">잘못된 생각</h2>
<p>최근까지 javascript는 함수 호출 시
참조형이면 넘길 때도 call by reference로 넘겨지고,
원시형이면 call by value로 넘겨지는 줄 알고 있었다.</p>
<p>그러다 최근 질문이 들어와서 답해주려다 자세히 알아보니,
잘못 알고 있었다는 것을 깨달았다.</p>
<p>Call by Value &amp; Call by Reference
우선 흔히 알고 있는 두 가지 개념에 대해 살펴보자</p>
<h2 id="call-by-value">Call by Value</h2>
<blockquote>
<p>호출자가 실제 매개 변수를 평가하고 그 값을 호출자에게 전달하는 관례
호출자에서 값 매개 변수의 수정 내용은 호출자에서 볼 수 없다.</p>
</blockquote>
<h2 id="call-by-reference">Call by Reference</h2>
<blockquote>
<p>호출자(caller)가 함수에 전달한 실제 매개변수(actual parameter)가 변수일 경우, 컴파일러가 &#39;해당 변수의 주소&#39;를 호출된 함수(callee)에 전달한다. 호출된 함수 내에서 해당 매개 변수에 접근할 때, 실제 변수의 주소를 이용하여 값을 변경할 수 있게 된다.
함수 내에서 변경된 값이 호출자에서 전달한 변수에도 반영되는 결과를 가져오게 되는 것이다.</p>
</blockquote>
<p>결론적으로 매개변수의 값이 아니라 주소가 전달되며, 이를 이용하여 함수 내에서 매개 변수의 값을 변경할 수 있다.</p>
<h2 id="js의-call-by-reference">JS의 Call by reference?</h2>
<p><u><strong>간단히 말하면 JS에는 Call by reference가 존재하지 않는다.</strong></u></p>
<p>이전까지는 초반에 언급했듯이
<strong>참조형이면 넘길 때도 call by reference</strong>로 넘겨지고,
<strong>원시형이면 call by value</strong>로 넘겨지는 줄 알고 있었다.</p>
<p>그래서 js는 c와는 다르게</p>
<p>매개변수의 타입이 참조형이면, call by reference 방식으로 넘겨주게 되어</p>
<p>callee 내에서 원본을 컨트롤할 수 있는 것이라고 생각했다.</p>
<p>아래의 예시로 착각했던 이유를 덧붙이겠다.</p>
<pre><code class="language-js">const test = (obj) =&gt; {
  obj.a = 23;
};

let b = { a: 1 };
console.log(b);
test(b);
console.log(b);</code></pre>
<p>위 코드는 함수 내에서 전달받은 객체의 값을 23으로 바꾸는 역할을 한다.</p>
<p>실제로 실행해보면,
함수 호출 전의 출력과 
호출 후의 출력이 변하여,</p>
<p><img src="https://velog.velcdn.com/images/bab_shunn/post/cf1ee548-2904-4735-b88f-a38067fb411c/image.png" alt=""></p>
<p>마치 call by reference에 의해 원본이 callee 내에서 수정된 것처럼 보인다.</p>
<p>사실 틀린 이해였다.</p>
<p>다른 예시를 이어서 보자.</p>
<pre><code class="language-js">const test = (obj) =&gt; {
  obj = { a: 23 };
};

let b = { a: 1 };
console.log(b);
test(b);
console.log(b);</code></pre>
<p>얼핏 보면 이전 예시와 같은 동작을 하는 것처럼 보인다.</p>
<p>그러나 실행 결과를 확인해보면,
<img src="https://velog.velcdn.com/images/bab_shunn/post/0c640ee5-4148-4f6e-908a-e0a8e98cddc4/image.png" alt=""></p>
<p>함수 실행 후에도 객체 b의 내용이 변하지 않은 것을 확인할 수 있었다.</p>
<p>그렇다면 지금 이게 무슨 상황인가?</p>
<h2 id="">??</h2>
<p>우선 call by value였다면 첫번째 예시가 설명되지 않는다.</p>
<p>원본에 영향을 미쳤기 때문이다.</p>
<p>그런데 call by reference로도 설명이 되지 않는다.</p>
<p>두번째 예시에선 원본에 영향을 미치지 않았기 때문이다.</p>
<p>찾아보니 아래와 같은 개념이 있었다.</p>
<h2 id="call-by-sharing">Call by Sharing</h2>
<blockquote>
<p>함수에 전달된 인수가 caller에서 볼 수 있는 방식으로 변형될 수 있지만,
인수로 전달된 특정 변수에 대한 엑세스는 제공되지 않는다.</p>
</blockquote>
<p>이게 무슨 말인가?할 수 있다.</p>
<p>_**즉 참조값을 직접적으로 전달하는 것이 아닌,</p>
<p>그 주소에 대한 복사본을 만들어서 전달하는 방식인 것이다.**_</p>
<p>위에서 봤던 예시와 함께 다시 정리해보자</p>
<p>(예시에서 등장하는 메모리 주소는 임의의 값이다)</p>
<h3 id="첫번째-예시">첫번째 예시</h3>
<pre><code class="language-js">const test = (obj) =&gt; {
  obj.a = 23;
};

let b = { a: 1 };
console.log(b);
test(b);
console.log(b);</code></pre>
<p>아래는 위 과정을 도식화한 것이다.
<img src="https://velog.velcdn.com/images/bab_shunn/post/64a2141c-65ea-45e4-9b25-19b5c627e9cd/image.png" alt=""></p>
<p>첫번째 예시는</p>
<p><strong>넘겨받은 주소의 복사본을 통해
점 연산자 ( &#39; . &#39; )를 사용하여 원본 객체 프로퍼티에 접근하여
새로운 값을 할당해준 것을 알 수 있을 것이다.</strong></p>
<h3 id="두번째-예시">두번째 예시</h3>
<pre><code class="language-js">const test = (obj) =&gt; {
  obj = { a: 23 };
};

let b = { a: 1 };
console.log(b);
test(b);
console.log(b);</code></pre>
<p>아래는 두번째 예시의 도식화 버전이다.
<img src="https://velog.velcdn.com/images/bab_shunn/post/2eaf3d4c-3b31-4822-a7d4-f554616ce9d7/image.png" alt=""></p>
<p>두번째 예시는</p>
<p>넘겨받은 주소의 복사본이 담겨있던 obj에 { a: 23 }이라는 새로운 값을 할당해준 것이기 때문에</p>
<p>다시 caller로 돌아왔을 때 0x01에는 여전히 { a: 1 }이 남아있는 것을 알 수 있을 것이다.</p>
<p>이렇게 Javascript는 call by sharing이라는 방식을 사용하고 있다</p>
<hr>
<p><strong>참고자료</strong>
<a href="https://hou27.tistory.com/entry/JS-call-by-reference%EA%B0%80-%EC%97%86%EB%8A%94-javascript">https://hou27.tistory.com/entry/JS-call-by-reference%EA%B0%80-%EC%97%86%EB%8A%94-javascript</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[3. 자바스크립트 배열은 배열이 아니다]]></title>
            <link>https://velog.io/@bab_shunn/3.-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%B0%B0%EC%97%B4%EC%9D%80-%EB%B0%B0%EC%97%B4%EC%9D%B4-%EC%95%84%EB%8B%88%EB%8B%A4</link>
            <guid>https://velog.io/@bab_shunn/3.-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%B0%B0%EC%97%B4%EC%9D%80-%EB%B0%B0%EC%97%B4%EC%9D%B4-%EC%95%84%EB%8B%88%EB%8B%A4</guid>
            <pubDate>Fri, 11 Apr 2025 04:04:03 GMT</pubDate>
            <description><![CDATA[<p>일반적으로 배열이라는 자료 구조의 개념은 동일한 크기의 메모리 공간이 빈틈없이 연속적으로 나열된 자료 구조를 말한다. 즉, 배열의 요소는 하나의 타입으로 통일되어 있으며 서로 연속적으로 인접해 있다. 이러한 배열을 밀집 배열(dense array)이라 한다.</p>
<p>일반적인 배열은 동일한 크기의 메모리 공간이 연속적으로 나열된 밀집 배열이다.</p>
<p>이처럼 배열의 요소는 동일한 크기를 갖으며 빈틈없이 연속적으로 이어져 있으므로 아래와 같이 인덱스를 통해 단 한번의 연산으로 임의의 요소에 접근(임의 접근(random access), 시간 복잡도 O(1))할 수 있다. 이는 매우 효율적이며 고속으로 동작한다.</p>
<p>검색 대상 요소의 메모리 주소 = 배열의 시작 메모리 주소 + 인덱스 * 요소의 바이트 수</p>
<p>예를 들어, 위 그림처럼 메모리 주소 1000에서 시작하고 각 요소의 크기가 8byte인 배열을 생각해 보자.</p>
<p>인덱스가 0인 요소의 메모리 주소 : 1000 + 0 * 8 = 1000
인덱스가 1인 요소에 메모리 주소 : 1000 + 1 * 8 = 1008
인덱스가 2인 요소에 메모리 주소 : 1000 + 2 * 8 = 1016
이처럼 배열은 인덱스를 통해 효율적으로 요소에 접근할 수 있다는 장점이 있다. 하지만 정렬되지 않은 배열에서 특정한 값을 탐색하는 경우, 모든 배열 요소를 처음부터 값을 발견할 때까지 차례대로 탐색(선형 탐색(linear search), 시간 복잡도 O(n))해야 한다.</p>
<p>// 선형 검색을 통해 주어진 배열(array)에 주어진 값(target)이 요소로 존재하는지 확인하여
// 존재하는 경우 해당 인덱스를 반환하고 존재하지 않는 경우 -1을 반환하는 함수
function linearSearch(array, target) {
  const length = array.length;</p>
<p>  for (let i = 0; i &lt; length; i++) {
    if (array[i] === target) return i;
  }</p>
<p>  return -1;
}</p>
<p>console.log(linearSearch([1, 2, 3, 4, 5, 6], 3)); // 2
console.log(linearSearch([1, 2, 3, 4, 5, 6], 0)); // -1
또한 배열에 요소를 삽입하거나 삭제하는 경우, 배열 요소를 연속적으로 유지하기 위해 요소를 이동시켜야 하는 단점도 있다.</p>
<p>배열 요소의 삽입과 삭제</p>
<p>자바스크립트의 배열은 지금까지 살펴본 일반적인 의미의 배열과 다르다. 즉, 배열의 요소를 위한 각각의 메모리 공간은 동일한 크기를 갖지 않아도 되며 연속적으로 이어져 있지 않을 수도 있다. 배열의 요소가 연속적으로 이어져 있지 않는 배열을 희소 배열(sparse array)이라 한다.</p>
<p>이처럼 자바스크립트의 배열은 엄밀히 말해 일반적 의미의 배열이 아니다. 자바스크립트의 배열은 일반적인 배열의 동작을 흉내낸 특수한 객체이다. 아래 예제를 살펴보자.</p>
<p>console.log(Object.getOwnPropertyDescriptors([1, 2, 3]));
/*
{
  &#39;0&#39;: { value: 1, writable: true, enumerable: true, configurable: true },
  &#39;1&#39;: { value: 2, writable: true, enumerable: true, configurable: true },
  &#39;2&#39;: { value: 3, writable: true, enumerable: true, configurable: true },
  length: { value: 3, writable: true, enumerable: false, configurable: false }
}
*/
이처럼 자바스크립트 배열은 인덱스를 프로퍼티 키로 갖으며 length 프로퍼티를 갖는 특수한 객체이다. 자바스크립트 배열의 요소는 사실 프로퍼티 값이다. 자바스크립트에서 사용할 수 있는 모든 값은 객체의 프로퍼티 값이 될 수 있으므로 어떤 타입의 값이라도 배열의 요소가 될 수 있다.</p>
<p>const arr = [
  &#39;string&#39;,
  10,
  true,
  null,
  undefined,
  NaN,
  Infinity,
  [ ],
  { },
  function () {}
];
일반적인 배열과 자바스크립트 배열의 장단점을 정리해보면 아래와 같다.</p>
<p>일반적인 배열은 인덱스로 배열 요소에 빠르게 접근할 수 있다. 하지만 특정 요소를 탐색하거나 요소를 삽입 또는 삭제하는 경우에는 효율적이지 않다.</p>
<p>자바스크립트 배열은 해시 테이블로 구현된 객체이므로 인덱스로 배열 요소에 접근하는 경우, 일반적인 배열보다 성능적인 면에서 느릴 수 밖에 없는 구조적인 단점을 갖는다. 하지만 특정 요소를 탐색하거나 요소를 삽입 또는 삭제하는 경우에는 일반적인 배열보다 빠른 성능을 기대할 수 있다.</p>
<p>즉, 자바스크립트 배열은 인덱스로 배열 요소에 접근하는 경우에는 일반적인 배열보다 느리지만 특정 요소를 탐색하거나 요소를 삽입 또는 삭제하는 경우에는 일반적인 배열보다 빠르다. 자바스크립트 배열은 인덱스로 접근하는 경우의 성능 대신 특정 요소를 탐색하거나 배열 요소를 삽입 또는 삭제하는 경우의 성능을 선택한 것이다.</p>
<p>이처럼 인덱스로 배열 요소에 접근할 때 일반적인 배열보다 느릴 수 밖에 없는 구조적인 단점을 보완하기 위해 대부분의 모던 자바스크립트 엔진은 배열을 일반 객체와 구별하여 보다 배열처럼 동작하도록 최적화하여 구현하였다.</p>
<p>아래와 같이 배열과 일반 객체의 성능을 테스트 해보면 배열이 일반 객체보다 약 2배 정도 빠른 것을 알 수 있다.</p>
<p>const arr = [];</p>
<p>console.time(&#39;Array Performance Test&#39;);</p>
<p>for (let i = 0; i &lt; 10000000; i++) {
  arr[i] = i;
}
console.timeEnd(&#39;Array Performance Test&#39;);
// 약 340ms</p>
<p>const obj = {};</p>
<p>console.time(&#39;Object Performance Test&#39;);</p>
<p>for (let i = 0; i &lt; 10000000; i++) {
  obj[i] = i;
}</p>
<p>console.timeEnd(&#39;Object Performance Test&#39;);
// 약 600ms</p>
<p>Next →
5.30 Higher order function
Back to top</p>
<p>© 2021  Ung-mo Lee</p>
<p>Hosted on GitHub Pages     Using MarkSheet</p>
<p>In his heart a man plans his course, but the LORD determines his steps.</p>
]]></description>
        </item>
    </channel>
</rss>