15분 안에 ToC를 구현해보자!


블로그 사이트를 개편하면서 ToC(Table of Contents)를 직접 만들어보고 싶었습니다.
글을 쓰기엔 짧은 경험이지만 누군가에게 도움이 될 수도 있으니 구현했던 과정을 공유하고자 합니다.

ToC 디자인은 MDN 사이트의 이쁜 디자인을 참고했습니다.


지금부터 Vanilla JS를 사용해서 ToC 예제를 구현해보겠습니다.
실습하듯이 따라 하셔도 좋고, 눈으로 슥슥 보셔도 됩니다.
제목에는 15분 만에 구현이라 썼지만 개념 설명이 추가되어 글이 다소 깁니다.
상세한 개념 설명은 링크 달아놓겠습니다.

자! 그럼 시작해볼게요.



1. 작업 소개

작업을 크게 4가지로 분류하여 진행하고자 합니다.

첫째, 페이지에 있는 h1, h2 태그를 가져온다.

마크다운을 사용하면 #이 h1 태그로, ##이 h2 태그로 변환됩니다. (###은 h3로, ####은 h4로)
저의 경우 h1 태그를 제목으로 h2 태그를 목차 제목으로 사용하고 있습니다.
그래서 h1, h2 태그 내용을 ToC에 넣을 예정입니다. h1, h2 태그를 가져오기 위해 document.querySelectorAll 함수를 사용할 겁니다.


둘째, h1, h2 태그 안의 내용을 토대로 ToC를 만듭니다.

ToC는 ul 태그와 li 태그로 구성하고자 합니다.
h1, h2 태그 내용은 textContent 속성으로 가져올 수 있습니다.
그리고 ToC는 스크롤에 의해 페이지가 내려가도 사이드 쪽에 위치하여 계속 존재하잖아요.
이 부분은 position: sticky 란 CSS 속성을 통해 구현할 겁니다.


셋째, ToC 내 목차 클릭 시 페이지가 이동되도록 한다.

ToC 내 목차를 클릭하면 페이지가 이동되도록 click 이벤트 핸들러를 추가하고자 합니다.
페이지 이동은 h1, h2 태그가 가지고 있는 scrollIntoView 함수를 사용할 겁니다.


마지막, 스크롤에 따라 h1, h2 태그에 해당하는 ToC 목차가 하이라이트 되도록 한다.

스크롤 시, 현재 스크린에서 보여지는 컨텐츠에 해당하는 ToC 목차를 하이라이트 되도록 합니다.
이 부분은 IntersectionObserver API를 통해 구현하고자 합니다.
만약 처음 들어본 API라면 이번 기회에 꼭 익혀보세요.
스크롤 이벤트와 그 짝궁인 스로틀, 바운싱에서 벗어날 수 있습니다!


실습을 마치면 하기 결과물이 만들어집니다.

최종 실습 결과물입니다.



2. 실습 준비

index.html 파일을 하나 만드셔서 하기 html 내용을 붙여넣으세요.
그리고 웹브라우저에서 index.html 파일을 열어보세요.

예시를 보시려면 여기를 클릭해주세요.

VS Code를 쓰시는 경우 Live Server 확장 프로그램을 활용하시면 브라우저에서 html 파일 여는 것이 편합니다.
확장 프로그램 설치 후 VS Code에서 index.html 파일을 열어 놓고, Ctrl+Shift+p를 눌러 Live Server를 입력하여 Live Server: Open With LiveServer 클릭하면 index.html이 반영된 브라우저가 열립니다.

https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ToC Example</title>
    <style>
      main {
        display: flex;
      }
      article {
        width: 70%
      }
      h1 {
        background-color: cadetblue;
      }
      h2 {
        background-color: aquamarine;
      }
      p {
        background-color: beige;
        height: 2000px;
      }
    </style>
  </head>
  <body>
    <main>
      <article>
        <h1>코끼리 위키백과</h1>
        <p>코끼리(elephant, 象)는 장비목에서 유일하게 현존하는 코끼리과를 구성하는 동물들의 총칭이다.</p>

        <h2>1. 어원</h2>
        <p>‘코끼리’는 ‘코가 긴 것, 코가 긴 짐승’이라는 뜻으로 ‘고ㅎ + 길- + -이’의 구성으로 만들어진 것이다.</p>

        <h2>2. 코끼리의 몸</h2>
        <h3>다리</h3>
        <p>몸 표면에는 굵은 털이 전체에 조잡하게 나 있으며, 꼬리 끝에는 줄모양의 긴 털이 나 있다.</p>
        <h3></h3>
        <p>코끼리의 코는 가장 활용을 많이하는 수단이다.</p>

        <h2>3. 생태</h2>
        <p>코끼리는 일반적으로 30~40마리씩 무리를 짓고 산다.</p>
        
        <h2>4. 인간과의 관계</h2>
        <p>사람들은 수천 년 전부터 코끼리를 길들여 이용했다.</p>
      </article>
    </main>
  </body>
</html>

html에는 코끼리에 대한 내용이 들어있습니다.
h1 태그는 코끼리 위키백과란 제목을 나타내고, h2 태그는 컨텐츠별 목차를 의미합니다.

css쪽을 보시면 main 태그에 display: flex 속성이 부여되어 있습니다.
다음 섹션에서 toc를 만든 다음, main 자손의 article 형제로서 toc를 추가할 예정입니다.
display: flex는 article과 toc를 정렬시켜줍니다.

flex는 정말 유용하게 잘 쓰이니 꼭 한번 익히시길 추천해 드립니다.

https://studiomeal.com/archives/197


이제 실습 준비는 끝났습니다.
지금부터 ToC 구현해보겠습니다.



3. 태그 가져오기

브라우저에서 F12를 눌러 개발자 도구를 엽니다.
콘솔창에 하기 JS 코드를 입력합니다.

JAVASCRIPT

// h1, h2 태그를 가져와 NodeList에 담아 반환한다.
const tags = document.querySelectorAll('h1, h2')
tags // 출력값 -> NodeList(5) [h1, h2, h2, h2, h2]

tags 변수에는 h1, h2 태그가 고스란히 담긴 NodeList가 할당되었습니다.
이제 이를 활용해 ToC에 넣으면 됩니다.



4. ToC 만들기

하기 html을 </aticle> 태그 아래에 넣고, css 소스는 style 태그 안에 넣어주세요.

예시를 보시려면 여기를 클릭해주세요.

HTML

<!-- ...</article> -->
<aside>
  <div class="toc">
    <ul>
      h1 들어갈 자리
      <li>h2 들어갈 자리</li>
      <li>h2 들어갈 자리</li>
      <li>h2 들어갈 자리</li>
      <li>h2 들어갈 자리</li>
    </ul>
  </div>
</aside>


CSS

.toc {
  position: sticky;
  top: 100px;
}

aside 태그 아래에 toc 클래스를 가진 div 태그를 생성하고, 그 자손으로 ul, li 태그들을 생성했습니다.
toc 클래스에 position: sticky 속성을 주었습니다.
한번 마우스 스크롤을 내려보세요.
페이지가 내려가도 ToC 위치가 잘 유지되나요?

sticky는 자신이 속해있는 부모 안에서 위치가 결정됩니다.
position: sticky 속성을 지정하면 top: 100px 속성은 다음과 같이 동작합니다.

우선 자신의 부모 태그를 찾는데 부모 태그는 block 요소(p, div 등)여야 합니다.
aside 태그는 block 요소이므로 toc의 부모로 여겨지고, aside 태그의 맨위(top)에서 100px 아래로 떨어진 곳에 자신의 위치(toc 위치)를 잡습니다.
그리고 스크롤을 내려도 그 위치에 고정되어 존재하는데 block 요소(p, div 등)를 만나면 그 자리에서 멈추어 스크롤을 내려도 더이상 위치가 내려가지 않습니다.

즉, sticky는 normal flow(absolute, fixed는 normal flow로 동작하지 않음)로 동작하면서 자신의 부모를 지정하여 그 부모 위치에 상대적으로 자신을 위치시킵니다.
normal flow, position에 대한 자세한 내용은 하기 MDN 문서를 참고해주세요.

https://developer.mozilla.org/en-US/docs/Web/CSS/position

이제 h1, h2 태그를 담아 놓은 tags 변수에 있는 내용을 ToC에 넣으면 됩니다.
ul, li 태그는 h1, h2 태그에 의해 동적으로 생성할 것이기 때문에 HTML에서 삭제해주세요.
그리고 TOC를 이쁘게 하기 위한 CSS를 추가하겠습니다.

HTML과 CSS를 수정하셨으면, 페이지를 새로고침하고 콘솔 창에 js 코드를 입력해봅니다.

HTML, CSS가 반영된 예시를 보시려면 여기를 클릭해주세요.

HTML

<!-- ...</article> -->
<aside>
  <div class="toc">
  </div>
</aside>


CSS

.toc {
  position: sticky;
  top: 100px;
  cursor: pointer;
}
.toc-title {
  padding-left: 10px;
  font-size: 18px;
  font-weight: bold;
  color: cadetblue;
}
.toc-item {
  padding: 5px 10px;
  list-style: none;
  font-size: 15px;
  color: black;
  border-left: 3px solid rgb(171, 171, 171);
}


JAVASCRIPT

const tags = document.querySelectorAll('h1,h2')
const toc = document.querySelector('.toc')

tags.forEach(tag => {
  if (tag.matches('h1')) {
    const ul = document.createElement('ul')
    ul.classList.add('toc-title')
    ul.textContent = tag.textContent
    toc.appendChild(ul)
  }
  else {
    const li = document.createElement('li')
    li.classList.add('toc-item')
    li.textContent = tag.textContent
    toc.firstElementChild.appendChild(li)
  }
})

콘솔 창에서 js 코드를 실행하면 ToC에 h1,h2 태그 내용이 반영됩니다.
h1,h2 태그를 리스트로 담고 있는 tagsforEach 함수를 사용하여 ToC에 h1,h2 태그 내용 할당 작업을 진행합니다.

forEach 함수 내부의 tag 변수가 h1 태그일 경우 ul 태그를 생성해서 클래스명과 text 내용을 반영하고, appendChild 함수를 통해 toc에 ul 태그를 자손으로 삽입합니다.

tag 변수가 h1 태그가 아닐 경우(여기에선 h2태그) li 태그를 생성해서 클래스명과 text 내용을 반영하고, toc의 첫 자손(위에서 생성한 ul 태그)의 자손으로 li 태그를 삽입합니다.

위 작업을 통해 ToC에 h1,h2 태그 내용이 반영됩니다.
이제 이벤트 처리를 해보겠습니다.



5. click 이벤트 추가하기

ToC 내 li 태그를 클릭했을 때 (ex. 1. 어원, 2. 코끼리의 몸) 클릭된 li 태그와 매칭되는 h1,h2 태그로 페이지를 이동시켜야 합니다.
click 했을 때 click한 대상이 몇번째 li 태그인지 파악하기 위해 index 값을 li 태그 속성에 저장합니다.
그리고 li 태그에 매칭되는 h1,h2 태그는 tags 변수에 리스트로 잘 담아두었으니, h1,h2 태그 속성에도 index 값을 저장합니다.
위에서 저장한 index 값을 활용하여 페이지를 이동시키면 됩니다.

ToC를 생성한 페이지에서 하기 js 코드를 콘솔 창에 입력해보세요.

JAVASCRIPT

// toc-title, toc-item 클래스명을 가진 태그(엘리먼트)의 dataset에 index 값을 저장한다. ex. data-index="0" 형태로 저장됨
const tocItems = document.querySelectorAll('.toc-title, .toc-item')
tocItems.forEach((item, index) => item.dataset.index = index),

// h1, h2 태그들에도 dataset에 indes 값을 저장한다. 다음 섹션인 IntersectionObserver에서 사용할 예정.
tags.forEach((tag, index) => tag.dataset.index = index)

// toc 클래스를 가진 div 태그(ul, li 태그의 조상)에 click 이벤트 핸들러를 추가한다. (이벤트 위임)
// scrollIntoView 이벤트를 통해 해당 태그로 이동한다.
toc.addEventListener('click', e => {
  if (e.target.matches('.toc-title') || e.target.matches('.toc-item')) {
    tags[e.target.dataset.index].scrollIntoView()
  }
})

이제 ToC 안에 있는 목차들을 클릭해보세요.
클릭된 목차에 해당하는 컨텐츠로 페이지가 잘 이동되나요?

document.querySelectorAll 함수를 통해 toc-title, toc-item 클래스 명을 가진 태그들을 가져온 다음 forEach를 통해 각 태그의 dataset에 index 값을 추가합니다.
item.dataset.index=1 할당문을 수행하면 item의 dataset에 암묵적으로 index가 생성되면서 값 1이 할당됩니다.
할당문을 통해 <li class="toc-item" data-index="1"></li> 이같은 형태가 만들어집니다.

그리고 ul, li 태그의 조상인 div 태그(toc 클래스)에 click 이벤트 핸들러를 추가합니다.
ul, li 태그마다 이벤트 핸들러를 추가하는 것은 메모리 공간을 많이 차지하기에 부모인 div 태그에만 이벤트 핸들러를 추가해줍니다.
자식이 가져야 할 이벤트 핸들러를 부모에게 위임한다고 하여 이 방식을 이벤트 위임이라고 부릅니다.

click 이벤트 핸들러 내부에서 click 이벤트 대상이 toc-title 클래스 혹은 toc-item 클래스 태그인지 확인합니다.
비록 현재 코드 기준에서 부모 태그 밑엔 toc-title, toc-item 클래스 태그만 있더라도 위 조건을 꼭 체크해주어야 합니다. 위 조건이 없다면 나중에 부모 태그 밑에 toc-title, toc-item 클래스가 아닌 다른 요소들이 추가되었을 때, click 이벤트 로직이 수행되어 의도치 않은 동작이 발생할 수 있습니다.

toc-title 클래스 혹은 toc-item 클래스 태그가 맞다면, 해당 태그의 dataset.index 값으로 tags 리스트를 참조하여 페이지가 이동할 태그를 가져옵니다.
그리고 scrollIntoView를 통해 해당 태그로 페이지를 이동시킵니다.
모든 태그(Element)는 scrollIntoView 함수를 가지고 있습니다.
scrollIntoView 함수 파라미터에 옵션 객체를 넣으면 스크롤 시 효과도 줄 수 있습니다.

tags[e.target.dataset.index].scrollIntoView({ behavior: "smooth", block: "center"})

위 js 코드에 나오듯 scrollIntoView 함수의 파라미터에 { behavior: "smooth", block: "center"} 객체를 추가하여 실행시켜보세요. 화면이 스무스하게 움직이면서 페이지가 맨 위가 아닌 중간으로 이동됩니다.

scrollIntoView에 대한 자세한 내용은 하기 URL을 참고해주세요.

https://developer.mozilla.org/ko/docs/Web/API/Element/scrollIntoView


이제 click 이벤트 핸들러 추가까지 완성했습니다.
지금까지 콘솔에 입력했던 js 내용은 html 파일 내에서 script 태그로 넣도록 하겠습니다.

HTML

<!-- ...  -->
<!-- </main> -->
<script>
  const tags = document.querySelectorAll('h1,h2')
  const toc = document.querySelector('.toc')

  tags.forEach(tag => {
    if (tag.matches('h1')) {
      const ul = document.createElement('ul')
      ul.classList.add('toc-title')
      ul.textContent = tag.textContent
      toc.appendChild(ul)
    }
    else {
      const li = document.createElement('li')
      li.classList.add('toc-item')
      li.textContent = tag.textContent
      toc.firstElementChild.appendChild(li)
    }
  })

  // toc-title, toc-item 클래스명을 가진 엘리먼트의 dataset에 index 값을 추가한다. ex. data-index="0" 형태
  const tocItems = document.querySelectorAll('.toc-title, .toc-item')
  tocItems.forEach((item, index) => item.dataset.index = index)

  // h1, h2 태그들에도 data-index를 부여해준다. IntersectionObserver에서 사용할 예정.
  tags.forEach((tag, index) => tag.dataset.index = index)

  // toc 클래스를 가진 div 태그(toc 목차들의 조상)에 click 이벤트를 건다. (이벤트 위임)
  // scrollIntoView 이벤트를 통해 해당 태그로 이동한다.
  toc.addEventListener('click', e => {
    if (e.target.matches('.toc-title') || e.target.matches('.toc-item')) {
      tags[e.target.dataset.index].scrollIntoView({ behavior: "smooth", block: "center"})
    }
  })
</script>

예시를 보시려면 여기를 클릭해주세요.



6. IntersectionObserver API 사용하기

이제 스크롤을 내릴 때 ToC 목차에 해당하는 컨텐츠 나오면 해당 목차가 하이라이트 되도록 구현해보겠습니다.

스크롤 이벤트 핸들러를 추가하여 구현하는 방법이 있지만 이 경우 스크롤 이벤트가 엄청나게 자주 발생되기 때문에 스로틀, 디바운싱을 통해 일정시간마다 스크롤 이벤트가 처리되도록 구현해줘야 합니다.
그리고 컨텐츠가 화면(스크린)에 들어왔는지 파악하는 로직도 구현해야하구요.
놀랍게도 IntersectionObserver API가 이 모든 것을 해결해줍니다.

사용법은 다음과 같습니다.
IntersetionObserver의 observer 객체를 생성합니다.
const observer = new IntersectionObserver() 이런 식으로요.
그리고 observer 객체가 가지고 있는 observe 메소드를 통해 관찰할 대상을 등록합니다. (여기에선 tags 변수에 있는 h1, h2 태그들이 되겠죠?)
observer.observer(h1태그) 이런 식으로요.

그럼 등록에 대한 할 일은 끝났습니다.
이제 스크롤을 내리거나 올릴 때 관찰하는 대상(tags 변수에 있는 h1, h2 태그들)이 화면에 들어오거나 나갈 때, IntersectionObserver가 이를 포착하여 우리에게 그 정보를 알려줍니다.
그 정보를 다루려면 IntersectionObserver를 생성할 때 첫 번째 파라미터에 넣는 callback 함수를 통해 가능합니다.
하기 코드를 통해 설명하겠습니다.

하기 css 코드를 추가하시고 페이지를 열어 콘솔 창에 js 코드는 입력해보세요.
그리고 스크롤을 쭉쭉 내려보세요.
h2 태그가 나올 때 마다 ToC 목차가 하이라이트 되는 게 보이나요?

하기 예시는 css만 추가하였으니, js는 콘솔 창에서 실행시켜주세요.
예시를 보시려면 여기를 클릭해주세요.

CSS

.toc-title.selected, .toc-item.selected {
  background-color: rgba(95, 158, 160, 0.3);
  color: black;
  border-left: 3px solid cadetblue;
}


JAVASCRIPT

let selectedIndex = 0
const observer = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      tocItems[selectedIndex].classList.remove('selected')
      selectedIndex = entry.target.dataset.index
      tocItems[selectedIndex].classList.add('selected')
    }   
  })
})

tags.forEach(tag => observer.observe(tag))

IntersectionObserver를 생성할 때 첫 번째 인자로 callback 함수를 넘깁니다.
callback의 첫 번째 파라미터로 리스트 형태인 entries가 넘어오는데 IntersercionObserver가 관찰하고 있는 대상 중에 화면에 들어왔거나 나간 대상들이 들어있습니다.

예를 들어 h1, h2 태그를 IntersectionObserver가 관찰하도록 observe 함수를 통해 등록해놓았다고 합시다.
그리고 스크롤을 내리는 도중 h1, h2 태그가 화면에 나타나지 않았다면 entries는 비어있는 리스트나 다름없습니다. (비어있다고 표현했지만 사실 콜백 함수가 실행되지 않겠죠)
h1 태그가 화면에 나타나면 entries 리스트에 h1 태그 정보가 담겨서 callback 함수가 실행됩니다.

화면에 나타난 h1 태그 정보에 대해 callback 함수는 딱 한 번 실행됩니다. (기본 옵션인 경우에 그렇습니다. threshold 옵션값에 따라 여러 번 호출될 수 있습니다.)
그리고 h2 태그가 나타나면 entries에 h2 태그가 담겨 callback 함수가 실행됩니다.
만약 스크롤을 내렸는데 우연히 h1 태그가 화면에서 사라지고, h2 태그가 화면에 나타나면 어떻게 될까요?
그럼 entries에 h1 태그와 h2 태그가 담기게 됩니다.

entries 리스트에 담긴 entry가 화면에 들어온 건지 나간 건지는 어떻게 알 수 있을까요?
그 내용은 entryisIntersecting 속성에 담겨 있습니다 isIntersecting 값이 true면 화면에 들어 온거고, false면 화면에서 나간 겁니다.

js 코드로 다시 돌아가 보면, if(entry.isIntersecting)을 통해 화면에 관찰하고 있는 태그가 들어왔는지 확인합니다.
만약 들어왔다면 selectedIndex 변수를 통해 기존에 선택된 태그의 selected 클래스를 없애주고, 화면에 들어온 태그의 index를 가져와, 해당 태그에 매칭되는 ToC 목차에 selected 클래스를 주어 하이라이트를 표시하게끔 합니다.

여기서 한가지 개선해야 할 부분이 있습니다.
페이지 맨 밑에서 스크롤을 위로 올려보면 4.인간과의 관계에서 3.생태로 올라갈 때 3.생태 컨텐츠를 읽고 있지만 ToC의 4.인간과의 관계 목차가 하이라이트를 유지하고 있는 현상을 볼 수 있습니다.
이는 Intersection Observer에서 3.생태 태그를 만나야 하이라이트 업데이트가 되어서 나타나는 현상인데요.
태그의 제목과 태그의 컨텐츠를 컨테이너로 감싸서 컨테이너를 Intersection Observer에 등록하면 이를 해결할 수 있습니다.
여기에선 다른 방식으로 구현해보겠습니다.

JAVASCRIPT

let selectedIndex = 0
const observer = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      tocItems[selectedIndex].classList.remove('selected')
      selectedIndex = entry.target.dataset.index
      tocItems[selectedIndex].classList.add('selected')
    }   
    else if(entry.boundingClientRect.y > 100) {
      tocItems[selectedIndex].classList.remove('selected')
      selectedIndex = Number(entry.target.dataset.index) - 1
      tocItems[selectedIndex].classList.add('selected')
    }
  })
})

tags.forEach(tag => observer.observe(tag))

if(entry.boundingClientRect.y > 100) 조건문을 추가했습니다.

boundingClientRect.y는 현재 entryy 위칫값을 알려줍니다.
화면에서 최상단은 y0이고, 아래로 갈수록 y 값이 커집니다.

스크롤을 내려서 entry가 화면 밖으로 나간 거라면 entry의 위치 y 값은 0 이하 값이 됩니다.
반대로 스크롤을 올려서 entry가 화면 밖으로 나간 거라면 entryy 값은 화면 세로 사이즈보다 클 것입니다.
화면의 맨 하단보다 아래에 있을 거니까요.

이 조건문에선 러프하게 100px로 잡았습니다.
스크롤을 내린 건지 올린 건지만 파악하면 되어서요.
스크롤을 위로 올렸기에 하이라이트가 될 목차는 현재 하이라이트된 (화면에서 나간) 목차 index-1index를 가질겁니다.
그래서 selectedIndex = Number(entry.target.dataset.index) - 1 값으로 selectedIndex를 할당했습니다.


지금까지 구현한 최종 예제입니다!


IntersectionObserver에는 다양한 기능이 있습니다.
컨텐츠의 25% 부분만 나와도(나갈 땐 75%부분) IntersectionObserver가 인식하게끔 할 수 있고, 들어왔는지 나갔는지 체크할 범위를 root로 지정할 수도 있습니다.

자세한 내용은 하기 MDN 사이트를 확인해주세요.

https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API


이로써 Vanilla JS로 진행한 ToC 구현이 마쳤습니다.
이 포스트가 도움이 되었기를!






정규표현식의 유용한 패턴
#집합찾기 #반복찾기 #역참조 #조건달기
A 액티비티에서 B 액티비티로 데이터 전달하기
#WorkflowFoundation #InArgument #OutArugment #Variables
Microsoft UI Automation Framework 이해하기
#AutomationElement #TreeWalker #ControlPatterns
AppDomain 탐구
#AppDomain #격리 #어셈블리별도실행 #플러그인시스템
Chrome Extension 활용하여여 웹 페이지 XPath 정보 가져오기
#ChromeExtension #크롬확장프로그램 #XPath
Workflow 디자이너와 액티비티의 관계
#WorkflowFoundation #Designer #Activity #ModelItem
ExpressionTextBox와 ModelItem의 바인딩 관계
#WorkflowFoundation #ExpressionTextBox #ModelItem #Binding
Windows 레지스트리 간단 정리
#Windows #Registry #레지스트리
WPF MVVM 패턴, 그리고 Binding
#WPF #MVVM #Binding #Modle #View #ViewModel
오라클 SQL 성능 최적화 - 바인드 변수와 Shared Memory 이해하기
#Oracle #바인드변수 #SharedMemory #LibraryCache #SqlPlan
ActivityDesigner와 CodeActivity 이해하기
#WorkflowFoundation #ActivityDesigner #CodeActivity
Selenium Implicit vs Explicit - 웹 요소 기다리기
#Selenium #IWebDriver #Implicit #Explicit
.NET에서 Selenium 활용하기 - 3가지 실전 예제
#Selenium #ChromeDriver #FindElements
.NET에서 CommandBinding 활용하기
#.NET #CommandBinding #디자인패턴
워크플로우 파운데이션(Workflow Foundation) 소개
#WorkflowFoundation #소개 #기본개념
AWS SAA 준비 - (4) 비용에 최적화된 아키텍처 설계
(정리) Exam Readiness - AWS Solutions Architect Associate
AWS SAA 준비 - (3) 안전한 아키텍처
(정리) Exam Readiness - AWS Solutions Architect Associate
AWS SAA 준비 - (2) 성능이 뛰어난 아키텍처 설계
(정리) Exam Readiness - AWS Solutions Architect Associate
AWS SAA 준비 - (1) 복원력을 갖춘 아키텍처 설계
(정리) Exam Readiness - AWS Solutions Architect Associate
15분 안에 ToC를 구현해보자!
Vanilla JS로 Table of Contents 구현하기
모듈
모던 자바스크립트 Deep Dive | 48장 | 모듈
에러 처리
모던 자바스크립트 Deep Dive | 47장 | 에러 처리
제너레이터와 async/await
모던 자바스크립트 Deep Dive | 46장 | 제너레이터와 async/await
프로미스
모던 자바스크립트 Deep Dive | 45장 | 프로미스
REST API
모던 자바스크립트 Deep Dive | 44장 | REST API
Ajax
모던 자바스크립트 Deep Dive | 43장 | Ajax
비동기 프로그래밍
모던 자바스크립트 Deep Dive | 42장 | 비동기 프로그래밍
타이머
모던 자바스크립트 Deep Dive | 41장 | 타이머
Set과 Map
모던 자바스크립트 Deep Dive | 37장 | Set과 Map
디스트럭처링
모던 자바스크립트 Deep Dive | 36장 | 디스트럭처링
브라우저의 렌더링 과정
모던 자바스크립트 Deep Dive | 38장 | 브라우저의 렌더링 과정
스프레드 문법
모던 자바스크립트 Deep Dive | 35장 | 스프레드 문법
이터러블
모던 자바스크립트 Deep Dive | 34장 | 이터러블
7번째 데이터 타입 Symbol
모던 자바스크립트 Deep Dive | 33장 | 7번째 데이터 타입 Symbol
String
모던 자바스크립트 Deep Dive | 32장 | String
RegExp
모던 자바스크립트 Deep Dive | 31장 | RegExp
Date
모던 자바스크립트 Deep Dive | 30장 | Date
Math
모던 자바스크립트 Deep Dive | 29장 | Math
DOM
모던 자바스크립트 Deep Dive | 39장 | DOM
Number
모던 자바스크립트 Deep Dive | 28장 | Number
배열
모던 자바스크립트 Deep Dive | 27장 | 배열
이벤트
모던 자바스크립트 Deep Dive | 40장 | 이벤트
ES6 함수의 추가 기능
모던 자바스크립트 Deep Dive | 26장 | ES6 함수의 추가 기능
클래스
모던 자바스크립트 Deep Dive | 25장 | 클래스
this
모던 자바스크립트 Deep Dive | 22장 | this
빌트인 객체
모던 자바스크립트 Deep Dive | 21장 | 빌트인 객체
strict mode
모던 자바스크립트 Deep Dive | 20장 | strict mode
클로저
모던 자바스크립트 Deep Dive | 24장 | 클로저
프로토타입
모던 자바스크립트 Deep Dive | 19장 | 프로토타입
함수와 일급 객체
모던 자바스크립트 Deep Dive | 18장 | 함수와 일급 객체
실행 컨텍스트
모던 자바스크립트 Deep Dive | 23장 | 실행 컨텍스트
생성자 함수에 의한 객체 생성
모던 자바스크립트 Deep Dive | 17장 | 생성자 함수에 의한 객체 생성
프로퍼티 어트리뷰트
모던 자바스크립트 Deep Dive | 16장 | 프로퍼티 어트리뷰트
let, const 키워드와 블록 레벨 스코프
모던 자바스크립트 Deep Dive | 15장 | let, const 키워드와 블록 레벨 스코프
전역 변수의 문제점
모던 자바스크립트 Deep Dive | 14장 | 전역 변수의 문제점
스코프
모던 자바스크립트 Deep Dive | 13장 | 스코프
함수
모던 자바스크립트 Deep Dive | 12장 | 함수
원시 값과 객체의 비교
모던 자바스크립트 Deep Dive | 11장 | 원시 값과 객체의 비교
객체 리터럴
모던 자바스크립트 Deep Dive | 10장 | 객체 리터럴
타입 변환과 단축 평가
모던 자바스크립트 Deep Dive | 9장 | 타입 변환과 단축 평가
제어문
모던 자바스크립트 Deep Dive | 8장 | 제어문
연산자
모던 자바스크립트 Deep Dive | 7장 | 연산자
데이터 타입
모던 자바스크립트 Deep Dive | 6장 | 데이터 타입
표현식과 문
모던 자바스크립트 Deep Dive | 5장 | 표현식과 문
변수
모던 자바스크립트 Deep Dive | 4장 | 변수
Iteration와 Generator
코드스피츠 77 ES6+ 3화 참조
WHATWG 탄생 배경
WHATWG, W3C, HTML의 관련에 대한 역사
프론트엔드(FE) 면접 질문 정리
FE관련 면접 질문 및 답변 정리한 내용입니다.
쿠버네티스(kubernetes, k8s) 용어 정리
쿠버네티스(kubernetes, k8s) 용어 정리
젠킨스(Jenkins) 정리
젠킨스(Jenkins) 정리
Docker 용어 정리
Docker 용어 정리
Git 용어 정리
Git 용어 정리
반응형 웹 디자인(Responsive Web Design)
CSS responsive 에 대하여
JS this에 대하여
this에 대해 알아보자
SQL*PLUS에 대하여
SQL*PLUS 정의 및 사용방법
Oracle에서 SQL Plan 확인하기
Oracle에서 SQL Plan을 확인해보자