From a6e012f222c95b5a84ec3b70d1c07ce9dddcb785 Mon Sep 17 00:00:00 2001 From: soobing Date: Sat, 26 Oct 2024 12:31:32 +0900 Subject: [PATCH] Updates --- a11y/writing-css-with-accessibility-in-mind/index.html | 2 +- browser/browser-coordinate-size-api/index.html | 2 +- infra/docker-command/index.html | 2 +- .../a11y/writing-css-with-accessibility-in-mind/page-data.json | 2 +- page-data/browser/browser-coordinate-size-api/page-data.json | 2 +- page-data/index/page-data.json | 2 +- page-data/infra/docker-command/page-data.json | 2 +- page-data/posts/All/page-data.json | 2 +- page-data/posts/a11y/page-data.json | 2 +- page-data/posts/browser/page-data.json | 2 +- page-data/posts/cs/page-data.json | 2 +- page-data/posts/feature/page-data.json | 2 +- page-data/posts/framework/page-data.json | 2 +- page-data/posts/infra/page-data.json | 2 +- page-data/posts/javascript/page-data.json | 2 +- page-data/posts/next/page-data.json | 2 +- page-data/posts/page-data.json | 2 +- page-data/posts/react-query/page-data.json | 2 +- page-data/posts/react/page-data.json | 2 +- page-data/posts/test/page-data.json | 2 +- page-data/posts/translate/page-data.json | 2 +- page-data/posts/troubleshooting/page-data.json | 2 +- page-data/posts/typescript/page-data.json | 2 +- .../react/storybook-react-server-components/page-data.json | 2 +- page-data/sq/d/1956554647.json | 2 +- posts/All/index.html | 2 +- posts/a11y/index.html | 2 +- posts/browser/index.html | 2 +- posts/cs/index.html | 2 +- posts/feature/index.html | 2 +- posts/framework/index.html | 2 +- posts/index.html | 2 +- posts/infra/index.html | 2 +- posts/javascript/index.html | 2 +- posts/next/index.html | 2 +- posts/react-query/index.html | 2 +- posts/react/index.html | 2 +- posts/test/index.html | 2 +- posts/translate/index.html | 2 +- posts/troubleshooting/index.html | 2 +- posts/typescript/index.html | 2 +- react/storybook-react-server-components/index.html | 2 +- sitemap-pages.xml | 2 +- sitemap.xml | 2 +- 44 files changed, 44 insertions(+), 44 deletions(-) diff --git a/a11y/writing-css-with-accessibility-in-mind/index.html b/a11y/writing-css-with-accessibility-in-mind/index.html index 7f4177f0..93a4708f 100644 --- a/a11y/writing-css-with-accessibility-in-mind/index.html +++ b/a11y/writing-css-with-accessibility-in-mind/index.html @@ -578,4 +578,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/browser/browser-coordinate-size-api/index.html b/browser/browser-coordinate-size-api/index.html index 68609646..52be9b58 100644 --- a/browser/browser-coordinate-size-api/index.html +++ b/browser/browser-coordinate-size-api/index.html @@ -295,4 +295,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/infra/docker-command/index.html b/infra/docker-command/index.html index 8f71066d..c263e42b 100644 --- a/infra/docker-command/index.html +++ b/infra/docker-command/index.html @@ -546,4 +546,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/page-data/a11y/writing-css-with-accessibility-in-mind/page-data.json b/page-data/a11y/writing-css-with-accessibility-in-mind/page-data.json index 3de3c907..a8edfbfe 100644 --- a/page-data/a11y/writing-css-with-accessibility-in-mind/page-data.json +++ b/page-data/a11y/writing-css-with-accessibility-in-mind/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-blog-template-js", "path": "/a11y/writing-css-with-accessibility-in-mind/", - "result": {"data":{"cur":{"id":"49246cb3-c4b8-5aeb-a4c5-a1b737e89b51","html":"
\n

원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939

\n
\n

CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다.

\n

이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다.

\n

읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다.

\n

약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리즈 중 세 번째 글입니다. 관심이 있다면 특별한 순서 없이, 접근성을 고려하여 HTML 작성하기접근성을 고려하여 자바스크립트 작성하기를 지금이나 나중에 읽어보면 좋습니다.

\n
\n

제 첫 웹사이트를 만든 것은 약 17년 전이었고, 그때는 CSS가 아직 상대적으로 새로운 것이었습니다. 그 이후로 많은 것이 변했고, CSS는 이제 우리에게 웹을 스타일링하기 위한 놀라운 도구를 제공합니다. 우리는 Verdana에서 웹폰트로, 고정된 너비에서 반응형 웹 디자인으로, 테이블 기반 레이아웃에서 그리드로 넘어갔고, 이제는 border와 font 또는 shadow에 이미지를 사용할 필요가 없습니다. 우리는 사용자 정의 속성, 쿼리, calc() 및 많은 새로운 단위들을 가지고 있습니다. 물론 이것은 지난 몇 년간의 훌륭한 발전들 중 일부에 불과합니다.

\n

\"\"

\n

접근성을 고려하여 CSS 작성하기

\n

CSS를 사용하여 문제를 해결하는 무한한 방법과 다양한 속성이 우리의 삶을 더 쉽게 만들어주지만, 동시에 사용자의 경험을 악화시킬 수도 있습니다. 사실, 단 세 줄의 CSS 만으로 웹사이트에 접근하기 어렵게 만들 수 있습니다.

\n

이 글에서는 접근성 있는 CSS를 작성하는 데 도움이 될만한 기술과 고려 사항 그리고 접근방식을 모두 모았습니다. 이 컬렉션은 기본 개념과 잘 알려진 속성으로 시작하여, 끝에는 좀 더 새로운 것들을 다룹니다.

\n

예상했던 것보다 많은 내용을 담게 되어, 가장 관심 있는 섹션으로 바로 이동할 수 있도록 링크가 걸린 목차를 마련했습니다.

\n\n

즐겁게 읽어주세요!

\n

🔗 가독성 있는 텍스트에서 읽기 쉬운 텍스트로

\n

이미지, 아이콘, 동영상은 오늘날 웹 디자인에서 빼놓을 수 없는 요소이지만, 여전히 거의 모든 웹사이트에서는 텍스트가 콘텐츠의 대부분을 차지합니다. 텍스트는 어떤 기기에서든 읽을 수 있어야 하기 때문에, 폰트 속성을 스타일링하고, 테스트하며, 미세 조정하는 데 상당한 시간을 할애하는 것이 중요합니다.

\n

글꼴 크기 확대

\n

\"\"

\n

사용자가 화면에서 떨어진 거리에 따라 글꼴 크기는 확대해야 합니다 (출처: Marvel)

\n

한때 12px 글꼴 크기가 본문(body) 텍스트의 표준이었지만, 해상도가 높은 기기의 등장으로 평균 글꼴 크기는 한동안 15에서 18px 사이에 정착했습니다. 최근 몇 년간, 글꼴 크기는 다시 20px 이상으로 상승했으며, 이는 좋은 일입니다. 텍스트는 스마트폰에서 충분히 커야 하며, TV와 같은 큰 화면에서 멀리서도 읽을 수 있도록 화면 크기에 따라 확대해야 합니다.

\n

서체의 특성이 매우 다양하기 때문에 표준의 최소 크기를 정의하는 것은 의미가 없지만, 작은 화면 크기에 좋은 시작점은 아마도 18-20px일 것입니다.

\n

물론 글꼴 크기에 대해 더 많이 말할 수 있지만, 이 글에서 다루기에는 너무 많습니다. 자세한 내용은 Christian Miller당신의 Body 텍스트는 너무 작습니다를 읽어보시길 권장합니다.

\n

라인 높이(line-height) 설정

\n

브라우저의 기본 라인 높이는 대략 **1.2**입니다. 웹 콘텐츠 접근성 지침에 따르면, 텍스트 블록 내의 문단에서는 최소 **1.5**여야 합니다.

\n

\"\"

\n

*line-height가 1.2인 문단과 1.5인 문단 비교*

\n

문단 내 라인 높이가 조정된 텍스트는 가독성이 향상될 뿐만 아니라, 시각적으로도 꽤 더 매력적입니다.

\n

텍스트를 왼쪽 또는 오른쪽으로 정렬

\n

\"\"

\n

양쪽 정렬된 텍스트의 불규칙한 단어 간격

\n

양쪽 정렬이 왼쪽 또는 오른쪽 정렬된 텍스트보다 보기 좋다고 생각하는 사람들도 있지만, 이는 나쁜 관행으로 간주됩니다. text-align: justify는 같은 길이의 줄을 만들기 위해 단어 간격을 조정합니다. 이러한 불균일한 공백은 가독성을 해칠 수 있으며 매우 산만해질 수 있습니다. 필요한 경우 단어를 구분하는 것도 해결책이 될 수 있지만, CSS 하이픈은 잘 지원되지 않고 예상대로 작동하지 않을 수 있습니다.

\n

문단 너비 정의

\n

여러 출처에 따르면 디자이너들은 줄당 45에서 85자를 유지해야 한다고 합니다. 이상적인 문단 너비는 65자라고 여겨집니다.

\n

텍스트 블록의 너비를 정의할 때 ch 단위가 유용할 수 있습니다. 1ch는 숫자 0을 나타내는 문자의 너비와 동일합니다. 또한, font-family 또는 font-size가 변경되면 이에 따라 변경됩니다.

\n
p {\n  /* 최대 너비 65자 */\n  max-width: 65ch;\n}
\n

어떠한 종류의 반응형 타이포그래피 기술을 사용한다면, 매우 큰 화면에서 사이트를 테스트해야 합니다. 글꼴 크기에 제한이 없다면, 특정 뷰포트 크기에서 텍스트가 읽기 어려워질 수 있습니다. 제한을 설정하는 방법이나 반응형 타이포그래피에 익숙하지 않다면, Mike Riethmullers의 글 반응형 타이포그래피에 대한 정밀한 제어를 읽어보시기 바랍니다.

\n

🔗 가상 요소에 콘텐츠 신중하게 사용하기

\n

우리는 ::before::after라는 가상 요소를 사용하여 요소의 맨 처음이나 맨 끝에 CSS를 추가할 수 있습니다. 이것은 디자인 요소를 우리 컴포넌트에 추가하는 매우 일반적이고 편리한 방법을 제공하지만, content 속성을 사용하여 내용을 추가하는 것도 가능합니다. 관심사의 분리의 관점에서 보면, 우리는 이렇게 하지 않아야 합니다.

\n
h2 {\n  content: "DON'T DO THIS";\n}
\n

우리의 내용은 HTML 파일, 데이터베이스 또는 API에서 오는 것이지, CSS에서 오는 것이 아닙니다. 때때로 우리는 폰트 아이콘 또는 특수 문자와 같은 텍스트가 아닌 콘텐츠를 추가하기 위해 content 속성을 사용합니다. 그렇게 할 때, 일부 스크린 리더가 생성된 콘텐츠를 인식하고 설명한다는 것을 기억해야 합니다. 생성된 콘텐츠가 순전히 표현적인 경우 보조 기술에서 숨겨야 합니다. 예를 들어 aria-hidden을 사용할 수 있습니다.

\n
<span class="icon icon-key" aria-hidden="true"></span>
\n

🔗 화면만이 유일한 매체가 아니다

\n

우리가 디지털 시대에 살고 있음에도 불구하고, 사람들은 여전히 물건을 인쇄합니다. 당신의 페이지가 인쇄되거나 PDF로 저장될 때도 접근성이 좋고 사용하기 쉬워야 합니다. 우리가 해야 할 일은 CSS에 @media 블록을 추가하여 종이에 어울리지 않거나 의미가 없는 요소(네비게이션 또는 광고)들을 숨기거나 스타일을 조정하는 것입니다.

\n
@media print {\n  .header {\n    position: static;\n  }  nav {\n    display: none;\n  }\n}
\n

인쇄된 웹 페이지의 문제 중 하나는 링크가 완전히 쓸모없다는 것입니다. 왜냐하면 그것들이 어디로 이끄는지 알 수 없기 때문입니다. 다행히도 CSS는 속성의 값들을 드러내고 화면(이 경우에는 종이)에 표시하는 방법을 제공합니다.

\n
@media print {\n  a[href^="http"]:not([href*="mywebsite.com"])::after {\n    content: " (" attr(href) ")";\n  }\n}
\n

위의 코드는 href 속성이 있고 http로 시작하지만 mywebsite.com이 값에 포함되지 않은 모든 링크 옆에 href 속성의 값을 표시합니다.

\n

Firefox와 특히 Chrome은 인쇄용 스타일 시트를 테스트하고 디버깅하기 위한 도구를 제공합니다.

\n

더 깊이 파고들고 싶다면, 인쇄 스타일 작업을 위한 팁과 트릭들을 모아놓은 것이 있습니다.

\n

🔗 완전히 지원되지 않는 속성 값에 대한 대안

\n

가끔 우리는 특정 속성 값을 사용하고 싶지만, 일부 브라우저에서 지원하지 않기 때문에 사용할 수 없는 상황에 처합니다. 하지만 대안을 제공하는 한, 그것을 사용하는 것을 멈출 필요는 없습니다. 종종 쿼리나 다른 탐지 기능을 사용하지 않고도 할 수 있습니다.

\n

예를 들어, IE와 이전 버전의 Edge가 이해하지 못하는 vmax 단위를 사용하고 싶다고 가정해 봅시다.

\n
div {\n  width: 50vmax; /* IE와 Edge 이전 버전에서 작동하지 않음 */\n}
\n

대안을 제공하기 위해서, 덜 이상적이지만 브라우저가 이해할 수 있는 width 속성을 사용하면 됩니다. 예를 들어 width: 50vw처럼. 다음 줄에서 실제 원하는 값을 설정합니다.

\n
div {\n  width: 50vw;\n  width: 50vmax;\n}
\n

vmax를 이해하지 못하는 브라우저는 width: 50vw를 해석하고 width: 50vmax는 무시합니다. 반면에 vmax를 이해하는 브라우저는 먼저 width: 50vw를 해석한 다음 width: 50vmax를 해석합니다. vmax 선언이 vw 선언 다음에 오기 때문에, 유저는 vmax 로 설정한 버전이 적용 됩니다.

\n

🔗 콘텐츠를 숨기는 여러 가지 방법

\n

HTML의 제목들은 문서(document)의 개요를 잡는 데 매우 유용합니다. <h1>부터 <h6>까지의 제목을 사용함으로써, 브라우저와 다른 소프트웨어에게 문서가 어떻게 구성되어있고 각 부분들이 어떻게 연관되어 있는지 알려줍니다. 문서 개요를 가지는 것은 매우 중요합니다, SEO에 좋고 스크린 리더 사용자들이 사이트를 탐색하는 데 도움이 됩니다. 디자인에 제목이 없어도 제목이 있으면 좋을 것 같은 경우가 발생할 수 있습니다. 그것은 종종 디자인 자체가 구조를 전달할 때의 경우입니다. 이런 경우에는 마크업에서 제목을 단순히 제거하지 않고 시각적으로 숨깁니다. CSS가 있든 없든 문서의 구조가 명확해야 합니다.

\n

이것은 물론 단 하나의 예일 뿐이며, 폼에서 라벨을 시각적으로 숨기는 것은 또 다른 예입니다(UX 관점에서 라벨을 숨기는 것은 바람직하지 않습니다).

\n

CSS에서는 콘텐츠를 숨기는 여러 가지 방법이 있으며, 적절한 기술을 올바른 시나리오에 맞게 선택하는 것은 여러분에게 달려 있습니다.

\n

모든 사람으로부터 콘텐츠 숨기기

\n

hidden 속성을 사용하거나 visibility: hidden 또는 display: none을 설정함으로써 콘텐츠를 완전히 숨길 수 있습니다. 사용자는 볼 수 없으며 스크린 리더나 검색 엔진도 읽을 수 없습니다.

\n

시각적으로만 콘텐츠 숨기기

\n

시각적으로만 콘텐츠를 숨기는 것은 간단하지 않습니다. 스크린 리더는 여전히 접근 가능하게 해야 하며, 브라우저의 특이점을 다뤄야 하고 요소가 포커스될 때 무슨 일이 발생하는지 결정해야 합니다. 물론, 이미 다른 사람들이 이를 해냈고 해결책이 있습니다.

\n

제가 연구를 해본 결과, 많은 다양한 접근 방식이 있다는 것을 알게 되었습니다. 그래서 전문가들에게 의견을 물어보았고, 추천된 기술을 분석하여 무슨 일이 일어나는지 완전히 이해했습니다.

\n
.visually-hidden {\n  /* 일반적인 흐름에서 항목을 제거 */\n  position: absolute;\n  /* 잘못 발음되거나 뭉개지는 텍스트를 위한 해결책 */\n  white-space: nowrap;\n  /* 가능한 가장 작은 크기로 설정 (일부 화면 낭독기는 높이와 너비가 0인 요소를 무시함) */\n  width: 1px;\n  height: 1px;\n  /* 크기 조정 후 넘치는 콘텐츠 숨기기 */\n  overflow: hidden;\n  /* 요소의 크기를 변경할 수 있는 모든 속성 초기화 */\n  border: 0;\n  padding: 0;\n  /* 클리핑은 요소의 어떤 부분이 표시될지 정의함. */\n  /* 구식 clip 속성은 구형 브라우저를 위함 */\n  clip: rect(0 0 0 0);\n  /* 최신 브라우저를 위한 clip-path. inset(50%)는 콘텐츠를 사라지게 하는 내부 사각형을 정의함. */\n  clip-path: inset(50%);\n  /* 현재 아무도 정확히 왜 margin: -1px이 있는지 확실하지 않음. 게다가 이것이 문제를 일으킬 수 있음 (참조:https://github.com/h5bp/html5-boilerplate/issues/1985). */\n  margin: -1px;\n}
\n

이 클래스를 어딘가에 저장하고 시각적으로 콘텐츠를 숨기면서 보조 기술과 검색 엔진은 접근 가능하게 하고 싶을 때 사용하세요.

\n

스킵 링크

\n

앞의 클래스들은 스킵 링크로 사용하기에도 적합합니다. 스킵 링크는 초기에는 시각적으로 숨겨져 있지만 포커스될 때 보이는 링크입니다. 스크린 리더와 키보드 사용자들이 소개 콘텐츠를 건너뛰고 주요 콘텐츠로 바로 이동할 수 있도록 페이지의 첫 번째 항목 중 하나여야 합니다. 기본적으로 사용자를 페이지의 특정 부분으로 이동시키는 앵커 링크입니다.

\n

\"\"

\n

“Skip to content” 링크는 포커스될 때 보입니다

\n

코드 펜에서 직접 시도해보고, Tab을 눌러 건너뛰기 링크를 드러내보세요.

\n

의미론적으로 콘텐츠 숨기기

\n

때때로 시각적으로 콘텐츠를 표시하되 스크린 리더에서는 숨기는 것이 의미가 있습니다. 예를 들어 아이콘을 사용할 때 그렇습니다. 그런 경우에는 숨기고자 하는 요소에 aria-hidden 속성을 추가하고 true로 설정하세요.

\n
<button>\n  <span class="icon icon-hamburger" aria-hidden="true"></span>\n  <span class="text">Menu</span>\n</button>
\n

기타

\n

콘텐츠를 숨기는 다른 방법들도 있습니다. 예를 들어 음수 text-indent나 제로 font-size 또는 height 등입니다. 일부는 작동하지만 특정 주의사항이 있습니다. 자세한 내용은 webaim.org텍스트 숨기기 기술을 읽어보세요.

\n

🔗 나쁜 대비는 신뢰할 수 없다

\n

우리의 디자인은 가독성을 위해 텍스트와 배경 사이에 충분한 대비를 제공해야 합니다. 저시력자뿐만 아니라 시각 장애가 없는 사람들도 고대비에서 이익을 얻습니다. 맑은 날에 스마트폰을 밖에서 사용하는 것을 생각해 보세요.

\n

색상 대비란 무엇이며 왜 중요한가

\n

세계보건기구(World Health Organization)에 따르면 인구의 약 4%가 시각 장애가 있습니다. 남성의 7~12%와 여성의 1% 미만이 어떤 형태의 색상 시력 결함을 가지고 있습니다. 이러한 장애 중 많은 것이 대비에 대한 민감도를 감소시키고, 일부 경우에는 색상을 구별하는 능력까지 줄입니다.

\n

두 색상이 색상 휠의 다른 부분에서 올 때, 그 색상들은 대비를 이룹니다. 일반적으로 말해서, 두 색상의 차이가 클수록 대비가 높습니다. 웹 디자이너 및 개발자로서 우리에게는 단순히 대비 자체뿐만 아니라, 텍스트에 적용됐을 때 얼마나 잘 작동하는지가 중요합니다. 텍스트와 그 배경 사이의 대비는 적어도 중등도 저시력을 가진 사람들이 읽을 수 있을 정도로 높아야 합니다. 물론, 이 기준을 충족하는지 고민할 필요는 없습니다. 웹 접근성 이니셔티브(Web Accessibility Initiative, WAI)는 이를 측정하기 위한 비율을 정의했습니다.

\n

최소 대비 비율

\n

대비 비율은 특정 배경에서 특정 크기와 너비를 가진 텍스트의 대비가 얼마나 높은지를 알려줍니다. 비율은 1:1에서 21:1까지 다양할 수 있습니다. 비교된 두 색상이 동일하면 1:1이고, 검정과 흰색이 대립되는 경우 21:1입니다.

\n

\"\"

\n

#777777 색상의 텍스트가 #DDDDDD 배경에서 3.3:1 비율을 가집니다. (출처: 대비 비율)

\n

웹 콘텐츠 접근성 지침(WCAG) 2.0에 따르면, 배경과 그 텍스트(또는 텍스트 이미지) 사이에 최소 4.5:1의 대비 비율이 존재해야 합니다. 이는 24px 미만(굵지 않은 경우) 및 19px 미만(굵은 경우)인 텍스트에 적용됩니다. 더 큰 텍스트의 경우 3:1 비율이면 충분합니다. 이것들은 레벨 AA 기준을 충족하기 위한 최소 수치입니다. 레벨 AAA를 통과하려면 일반 텍스트의 최소 비율은 7:1이고 굵은 텍스트는 4.5:1입니다. 규정 준수를 위한 필수 사항은 아니지만, 우리가 아이콘을 사용한다면 텍스트 대비 규정을 충족하는 아이콘을 사용해야 합니다.

\n

저는 제 친구 Daniel에게 비율에 대해 말했고, 우리가 현재 작업 중인 프로젝트에서 그것을 올바르게 가져가는 것이 중요하다고 말했습니다. 다양한 조합을 시도한 후, 그는 이것이 생각했던 것보다 어렵다고 전화로 말했습니다. 문제는 충분히 시각적으로 매력적인 조합이 없는 것이 아니라, 지난 몇 년 동안 디자이너들이 저대비 조합을 사용하는 데 익숙해졌다는 것입니다. 작은 에이전시뿐만 아니라 애플이나 구글과 같은 큰 회사들도 이 불리한 디자인 추세를 따르고 있습니다.

\n
\n

나이가 확실히 내 시력에 영향을 미쳤지만, 나는 디자인 트렌드로 고통받고 있다.

\n
\n

Kevin Marks

\n

대비 비율을 계산하기 위한 공식이 있지만, 오래된 계산기를 꺼내서 계산할 필요는 없습니다. 도구들이 있습니다.

\n

대비 비율 측정

\n

Chrome Canary에서는 개발자 도구에서 직접 대비 비율을 표시할 수 있습니다. Remy Sharp가 블로그 글에서 이를 공유합니다.

\n

\"\"

\n

Chrome 개발자 도구에서의 대비 비율.

\n

색상 대비와 일반적인 접근성을 테스트하기 위한 많은 도구들이 있습니다. 다음 목록은 광범위하지는 않지만, 제가 선호하는 도구들의 작은 모음집 입니다.

\n

온라인

\n\n

브라우저에서 빠르고 쉬운 대비 체커

\n\n

브라우저에서 좀 더 많은 옵션을 가진 대비 체커

\n\n

브라우저 도구로 대비와 더 많은 것들을 체크

\n\n

자동 대비 체크가 있는 색상 선택기

\n

브라우저 확장 프로그램과 개발자 도구

\n\n

Chrome 60은 Lighthouse를 기반으로 한 새로운 감사 패널과 함께 출시되었습니다. 접근성 점수를 부여하고 문제를 나열합니다.

\n\n

대비, 문서 개요 등을 테스트하기 위한 훌륭한 브라우저 확장 프로그램.

\n\n

aXe Chrome 확장 프로그램을 사용하여 웹 사이트에서 접근성 결함을 자동으로 찾는 도구.

\n

기타

\n\n

“WCAG에 대한 두 레이어의 색상 대비를 계산하는 Sketch 플러그인.

\n\n

고대비의 경험

\n

고대비의 색상을 사용하는 것도 훌륭하지만, 저시력을 가진 사람들은 여전히 웹사이트에서 사용하는 색상을 변경하고 싶어할 수 있습니다. 사용자의 요구는 매우 다양하며 그에 따라 색상 변경 방법도 다양합니다. 이 사실은 어느 정도 예측 불가능성을 내포하고 있으며, 우리의 페이지들이 항상 완전한 접근성을 보장하기 어렵게 만듭니다. 그래서 우리는 단지, 대비 수준 AA 또는 AAA 기준을 충족하는 것에만 의존해서는 안 되며, 웹사이트를 철저히 테스트하고 대비가 높은 대안을 제공하는 것도 고려해야 합니다.

\n

윈도우에서의 고대비 모드

\n

윈도우에서는 설정에서 고대비 옵션을 사용할 수 있습니다. 사용자는 자신만의 색상 설정을 정의하거나 사전 정의된 테마를 선택할 수 있습니다.

\n

\"\"

\n

윈도우에서의 고대비 설정

\n

간단한 로그인 폼을 만들었고(첫 번째 스크린샷 중 하나. https://dribbble.com/shots/1687064-Simple-Login-Form으로 부터 영감을 받음.) 고대비를 가진 다양한 테마로 테스트해 보았습니다.

\n

\"\"

\n

고대비 설정에서의 다양한 로그인 폼

\n

Anika Henke는 사용자들이 웹사이트에서 색상을 어떻게 변경하는지에 대해 썼습니다. 그녀는 폼을 테스트하던 중 입력 필드가 보이지 않게 되었고 버튼이 인식되지 않게 되었다는 것을 발견했습니다. 위 스크린샷에서도 같은 일이 발생하는 것을 볼 수 있습니다. 대체 텍스트가 없었다면, 사용자들은 두 개의 입력 필드가 있다는 것을 알지 못했을 것입니다. 인풋과 버튼에 기본 테두리를 추가하는 것은 빠른 해결책이었습니다(브라우저 간 테스트되지 않음).

\n

\"\"

\n

고대비 설정에서 인풋과 버튼에 테두리가 있는 개선된 로그인 폼

\n

미디어 쿼리를 사용하여 고대비 모드가 활성화되었는지 감지하고 특정 스타일을 제공할 수 있습니다.

\n
/* 고대비 모드 활성화 */\n@media (-ms-high-contrast:active) {\n}/* 특정 검정-백색 테마의 고대비 모드 */\n@media (-ms-high-contrast:black-on-white) {\n}/* 특정 검정-백색 테마의 고대비 모드 */\n@media (-ms-high-contrast:white-on-black) {\n}
\n

Patrick H. Lauke 는 윈도우 고대비 모드: -ms-high-contrast의 제한된 유용성에서 이 미디어 기능에 대한 그의 생각과 우려를 공유했습니다. 이에 응답으로 Greg Whitworth는 다음과 같이 지적했습니다. “이 기능의 유일한 목적은 대비 민감도를 가진 사용자들에게 더 나은 경험을 제공하는 것입니다. 그러므로, 특정 색상이 무엇인지에 대해 반드시 신경 쓸 필요는 없습니다. 어느 정도까지는, 여러분의 사이트가 어떻게 보이는지보다 어떻게 고대비에서 기능하는지에 대해 신경 써야 합니다.”

\n

높은 대비 크롬 확장 프로그램

\n

구글 크롬을 위한 고대비 확장 프로그램도 있어, 사용자들이 텍스트를 읽기 쉽게 만드는 여러 고대비 색상 필터로 웹을 탐색할 수 있습니다.

\n

고대비의 대안

\n

디자인의 일부분이 충분한 대비를 가지고 있지 않더라도, Alternate Version 조항을 사용하여 WCAG 기준을 충족할 수 있습니다. 이에 따르면, 사용자에게 페이지의 고대비 버전으로 연결하는 링크나, 페이지의 모든 측면이 준수하도록 페이지를 변경할 수 있는 페이지 상의 컨트롤을 제공해야 합니다.

\n

이 대안에 대한 몇 가지 기준이 있습니다.

\n\n

NoCoffee로 테스트하기

\n

\"\"

\n

NoCoffee는 저시력, 색상 결핍 및 차단된 시각 영역을 시뮬레이션합니다

\n

기준을 충족하는 것과 실제 사람을 대상으로 테스트하는 것은 별개의 문제입니다. 모든 사람이 전문적인 테스트 수단을 가지고 있는 것은 아닙니다. 다행히도, NoCoffee는 저시력, 색상 결핍 및 차단된 시각 영역을 쉽게 시뮬레이션할 수 있는 방법을 제공합니다. 이는 경미한~극심한 시력 문제를 가진 사람들이 직면하는 문제를 이해하는 데 도움이 될 수 있습니다.

\n

🔗 색상이 정보의 유일한 단서가 되어서는 안 된다

\n

앞서 언급했듯이, 많은 남성들이 시력 결함을 가지고 있습니다. 유형도 다양합니다. 가장 흔한 유형 중 하나인 중색 이상(Deuteranomaly)은 빨강과 녹색을 구분하기 어렵게 만듭니다. 색상 시력 결함이 있는 사람은 인터페이스를 사용할 수 없게 될 수 있으므로 색상만을 시각적 단서로 사용하는 것을 피해야 합니다.

\n

이전 예시에서 보여준 폼의 입력 필드에 성공 및 오류 상태를 나타내는 테두리를 추가했습니다. 다음 스크린샷은 색상만으로 사용자에게 충분한 피드백을 주지 못한다는 것을 보여줍니다. 테두리 색상이 전혀 보이지 않거나 잘못 보이는 경우가 있습니다.

\n

\"\"

\n

고대비 모드에서 색상만으로 폼의 성공과 실패를 구별하는 것은 효과적이지 않습니다.

\n

간단한 아이콘을 추가하면 접근성과 사용자 경험을 개선하는 데 도움이 될 수 있습니다.

\n

링크도 비슷한 예시 중 하나입니다. 링크는 색상만으로 일반 텍스트와 구별되어서는 안 됩니다. 링크에 밑줄을 유지하는 것이 좋습니다.

\n

🔗 순서에 신경 쓰기

\n

아이템들을 배치 순서를 바꾸는 방법은 많이 있습니다. 예를 들어, Flexbox에는 orderflex-direction이 있고, Grid에는 order, flex-auto-flow 및 명시적 배치가 있습니다. 이러한 속성들은 매우 유용하지만, 콘텐츠의 DOM 순서와 시각적 표현 사이의 연결이 끊어질 수 있습니다.

\n

다음 예시에서는 여러 그리드 속성을 사용하여 배치된 갤러리의 이미지를 볼 수 있습니다.

\n\n

처음에는 문제가 없어 보이지만, 키보드를 사용하여 이미지에서 이미지로 이동할 때 순서를 전혀 예측할 수 없다는 것을 알 수 있습니다. Tab 키를 눌렀을 때 다음에 어떤 이미지가 강조될지 알 수가 없습니다. 여기에 포커스 스타일이 없으면 최악의 상황이 될 수 있습니다.

\n\n

예측 불가능하거나 잘못된 순서는 키보드 사용자뿐만 아니라 스크린 리더 사용자에게도 문제가 됩니다. 스크린 리더는 DOM 순서대로 콘텐츠를 표시하므로 소프트웨어는 CSS 순서에 영향을 받지 않지만 사용자는 영향을 받습니다. 스크린 리더 사용자는 콘텐츠의 시각적 표현에 신경 쓰지 않는다고 생각할 수 있지만, 모든 스크린 리더의 사용자가 시각 장애가 있는 것은 아닙니다. 일부는 저시력이나 학습 장애가 있어 화면에 표시되는 내용을 보완하기 위해 스크린 리더를 사용합니다.

\n

이 순서 문제는 플렉스나 그리드 아이템뿐만 아니라 모든 종류의 위치 지정에도 적용됩니다. 스타일 없이도 의미가 있는 방식으로 콘텐츠를 정렬하는 것이 중요하며, 디자인에서의 순서와 일치하는지 확인해야 합니다. 일치하지 않으면 디자인을 다시 생각해야 할 수 있습니다. CSS에서 올바르게 위치시키지 못한다고 해서 마크업에서 요소들을 임의로 재배열하지 마세요.

\n

Rob Dodson’s의 콘텐츠 재배열이 접근성에 영향을 미치는가?와 Adrian Roselli의 코드의 순서가 중요하다 글을 참고해 더 자세한 내용을 알아보세요.

\n

🔗 중요한 것에 집중하기: focus

\n

저는 이미 키보드 탐색 기초와 포커스 가능한 요소들에 대해 접근성을 고려하여 자바스크립트 작성하기라는 글을 썼습니다. 이 주제가 처음이시라면, 그 글을 빠르게 읽고 오세요.

\n

키보드를 사용하여 웹사이트를 탐색할 수 있도록 하는 것은 중요합니다. 많은 사용자들이 웹 서핑을 할 때 키보드에 의존합니다. 그들 중에는 운동 장애가 있는 사람들, 시각 장애가 있는 사람들, 손이 없거나 어떤 이유로든 마우스나 트랙패드를 사용할 수 없는 사람들이 있습니다.

\n

CSS를 사용해서 포커스 가능한 요소들에 스타일을 적용할 수 있는 몇 가지 방법이 있습니다.

\n

포커스된 아이템 선택하기**

\n

:focus 의사 클래스를 사용하여 포커스 가능한 아이템들이 포커스 되었을때 스타일을 적용할 수 있습니다.

\n
a:focus {\n  background-color: #000000;\n  color: #FFFFFF;\n}
\n

기본 포커스 스타일은 브라우저마다 일관성이 없으며 보기 좋지 않은 경우가 많으며, 때로는 디자인과 잘 어울리지 않습니다. 사용자 경험을 개선하고 디자인에 맞는 맞춤 포커스 스타일을 제공하는 것이 좋습니다.

\n

하지만 무엇을 하든, 대체 스타일을 제공하지 않고 기본 윤곽선(점선 윤곽, 파란색 또는 주황색 반지)만 제거하지 마세요. 주로 키보드로 탐색하는 사용자들은 포커스 위치를 알 수 없다면 사이트를 사용할 수 없게 됩니다.

\n

\"\"

\n

대안 없이 기본 포커스 스타일을 제거하지 마세요 (출처: outlinenone.com)

\n

이것은 단순한 팁이 아니라 레벨 AA 기준 기준입니다.

\n

키보드와 마우스 사용자 구분하기

\n

이미 언급했듯이, 디자이너들이 좌절하는 것 중 하나는 브라우저 간 포커스 스타일의 일관성이 부족하다는 것입니다. 또한 마우스를 사용할 때도 일부 포커스 가능한 요소들에서 포커스 스타일이 보이는 점도 문제입니다. 때로는 마우스 사용자에게는 포커스 스타일을 보여줄 필요가 없으며, 심지어 방해가 되거나 미학적으로 불쾌할 수 있습니다.

\n

\"\"

\n

컨텐츠 영역이 클릭되었을 때 크롬에서 파란색 윤곽선이 보이는 맞춤 탭 컴포넌트 (출처: frend.co)

\n

웹 페이지의 특정 요소에 포커스가 가 있을 때 outline 속성을 제거해서는 안됩니다. 왜냐하면 컴포넌트가 더 이상 키보드 사용자에게 접근 불가능하기 때문입니다. 우리에게 필요한 것은 키보드와 마우스 사용을 구별하는 방법입니다. 이것은 CSS Level 4 선택자 명세의 일부인 :focus-ring 가상 클래스를 사용하여 가능 합니다. ”:focus-ring 가상 클래스는 요소가 :focus 가상 클래스와 일치하고, 사용자 에이전트(브라우저)가 휴리스틱(규칙이나 패턴)을 통해 요소에 특별히 표시되어야 함을 결정할 때 (일반적으로 ‘focus-ring’을 통해) 적용됩니다.” (출처: CSS 선택자 레벨 4 초안)

\n
/* 기본 윤곽선 제거 */\n:focus {\n  outline: none;\n}\n\n/* 윤곽선이 보여야 할 때만 추가 */\n:focus-ring {\n  outline: 2px solid blue;\n}
\n

아쉽게도 현재 어떤 브라우저도 :focus-ring의 표준 구현을 지원하지 않습니다. (Firefox는 -moz-focus-ring을 지원함), 하지만 적절할 때 .focus-ring 클래스를 추가하는 경량 폴리필이 있습니다.

\n
/* 자바스크립트가 활성화되어 작동하고, .focus-ring 클래스가 없는 \n모든 포커스 가능한 요소를 선택하여 윤곽선을 제거합니다 */\n.js-focus-ring :focus:not(.focus-ring) {\n    outline-width: 0;\n}
\n

자세한 내용은 Rob Dodson의 a11ycasts 에피소드, Focus Ring!을 시청하세요.

\n

포커스된 자식이 있는 요소에 대한 스타일링

\n

:focus-within는 상대적으로 새로운 가상 클래스로, 이미 대부분의 주요 브라우저에서 지원되고 있습니다. 이 클래스를 사용하면 현재 포커스된 자식 요소를 가진 요소를 선택할 수 있습니다.

\n

\"\"

\n

자식 항목 중 하나가 포커스되면 그림자가 표시되는 예시입니다.

\n
form:focus-within {\n  box-shadow: 0 0 4px 6px rgba(80,88,156,0.2);\n}
\n

이 기능을 CodePen에서 확인할 수 있습니다.

\n

포커스에 대한 자세한 내용은 YouTube에서 포커스란 무엇인가요? 영상을 참조하세요.

\n

🔗 그리드와 평평한 문서 구조

\n

새로운 사이트를 만들 때, 우리는 보통 HTML 작성부터 시작합니다. 적절한 마크업을 선택하고 요소들을 논리적 순서에 맞게 배치합니다. 문서가 올바르게 작성되어 있고 잘 구조화되어 있으며 순서가 의미 있게 되면 CSS를 추가합니다. CSS Grid Layout이 나오기 전에는, 특히 DOM 순서와 디자인 순서가 일치하지 않은 경우에 레이아웃을 만드는 것이 매우 까다로웠습니다. float, position 그리고 때때로 Flexbox조차도 어떤 상황에서는 충분히 유연하지 않았고, 우리는 DOM 순서를 변경하고 싶은 유혹에 빠지곤 했습니다. Grid의 명시적 배치와 해당 영역 덕분에 아이템을 위치시키는 데 필요한 모든 유연성을 갖게 되었습니다. 이것은 훌륭하지만, Grid는 문서 구조를 해칠 수 있는 새로운 유혹을 도입합니다.

\n

다음과 같은 디자인을 가지고 있고, 그 아이템들에 h2ul을 사용한다고 가정해 봅시다. 왜냐하면 그것이 당신에게 가장 의미가 있기 때문입니다.

\n

\"\"

\n

제목과 목록이 있는 레이아웃

\n
<div class="wrapper">\n  <h2>Heading</h2>\n  <ul>\n    <li><a href="#">Element 1</a></li>\n    <li><a href="#">Element 2</a></li>\n    <li><a href="#">Element 3</a></li>\n    <li><a href="#">Element 4</a></li>\n    <li><a href="#">Element 5</a></li>\n    <li><a href="#">Element 6</a></li>\n  </ul>\n</div>
\n

이러한 요소들을 열에 넣고 <h2>를 위치시키는 것은 꽤 쉽습니다… 또는 적어도 그렇게 보입니다.

\n
.wrapper {\n  display: grid;\n  grid-template-columns: 120px repeat(2, 1fr);\n  grid-gap: 20px;\n}h2 {\n  grid-column: 2 / -1;\n}
\n

\"\"

\n

제목과 목록이 있는 레이아웃. 그리드 컨테이너의 직접적인 자식만 그리드에 배치됩니다.

\n

하지만 예상과는 달리 보입니다. 문제는 그리드 컨테이너의 직접적인 자식만이 그리드에 배치된다는 것인데, 이 경우에는 <h2><ul>이 해당됩니다. 하지만 여러분은 <li>들을 그리드 아이템으로 만들고 싶습니다. 이 문제에 대한 최악의 해결책은 구조를 단순화시켜 <ul>을 제거하고 <li><div>로 변환하여 그리드 컨테이너의 직접적인 자식으로 만드는 것입니다.

\n

가장 좋은 해결책은 <ul>display 속성을 subgrid로 설정하는 것이지만, 불행히도 subgrid는 명세의 레벨 1에 포함되지 않았고 우리는 그것이 출시될 때까지 더 기다려야 합니다.

\n

<ul>display: contents를 사용할 수 있지만, 현재 Firefox만이 이를 지원합니다. display: contents는 요소의 자식들이 마치 요소의 부모의 직접적인 자식인 것처럼 보이게 만듭니다.

\n

결국, **<ul>**에 다른 그리드를 정의해야 합니다. 이것은 이상적이지 않지만, 문서의 구조를 단순화시키고 의미를 해치는 것보다는 낫습니다. 이것은 매우 기본적인 예제이며 목록이 전체 그리드를 차지하기 때문에 부모 그리드에서 일부 값을 상속받을 수 있습니다.

\n
ul {\n  /* 전체 그리드를 차지함 */\n  grid-column: 1 / -1;  /* 다른 그리드를 생성하고 부모 그리드에서 값을 상속받음 */\n  display: inherit;\n  grid-template-columns: inherit;\n  grid-gap: inherit;  /* display: contents를 이해하는 브라우저를 위해 display 덮어쓰기 */\n  display: contents;\n}
\n

CodePen에서 두 가지 솔루션을 볼 수 있습니다.

\n

결론

\n

이 글에서 꽤 많은 내용을 다루고 있지만 CSS와 접근성에 대해 알아야 할 모든 것을 다루지는 않습니다. 그러나 이것은 단순한 출발점 그 이상입니다. DOM 및 포커스 순서를 올바르게 설정하고 고대비에 신경 쓰고 일반적으로 접근성을 고려하여 디자인 하는 것만으로도 이미 훌륭한 작업을 하고 있는 것입니다. 새로운 페이지나 사이트를 만들 때마다 접근성을 약간만 더 고려한다면 웹을 더 나은 곳으로 만들 수 있습니다.

\n
\n

제약사항을 고려하여 디자인하는 것은 단순히 잘 디자인하는 것이다.

\n
\n

Aaron Gustafson

\n

이 글을 즐겁게 읽으셨고 새로운 것을 배우셨기를 바랍니다. 질문이나 피드백이 있으시면 댓글을 남겨주시거나 트위터를 통해 연락해 주시기 바랍니다.

\n

이 글을 작성하는 데 도움을 주신 멘토 Aaron Gustafson에게 감사드립니다.

\n

더 많은 접근성 팁

\n

이 글은 네 부분 시리즈 중 세 번째입니다. 마지막 글은 준비 중이며 곧 공개될 예정입니다.

\n
    \n
  1. 접근성을 고려한 HTML 작성하기
  2. \n
  3. 접근성을 고려한 JavaScript 작성하기
  4. \n
  5. 접근성을 고려한 CSS 작성하기
  6. \n
  7. 다음 글: 접근성을 고려한 디자인 및 개발 방법 배우기
  8. \n
\n

독자 여러분의 독서와 이 글을 좋아하시고 공유해 주시면 감사하겠습니다.

\n

제가 작성한 다른 글도 확인해보실 수 있습니다.

\n

Progressively Enhancing CSS Layout: From Floats To Flexbox To Grid

\n

The Difference Between Explicit and Implicit Grids

\n

추가적인 읽을 거리와 자료들

\n

가독성 있는 텍스트 작성법

\n\n

가상 요소에 콘텐츠 신중하게 사용하기

\n\n

화면만이 유일한 매체가 아니다

\n\n

완전히 지원되지 않는 속성 값에 대한 대안

\n\n

콘텐츠를 숨기는 여러 가지 방법

\n\n

나쁜 대비는 신뢰할 수 없다

\n\n

색상이 정보의 유일한 단서가 되어서는 안 된다

\n\n

순서에 신경 쓰기

\n\n

중요한 것에 집중하기: focus

\n\n

그리드와 평평한 문서 구조

\n\n","excerpt":"원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939 CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다. 이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다. 읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다. 약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리…","frontmatter":{"date":"February 18, 2024","title":"(번역) 접근성을 고려하여 CSS 작성하기","categories":"translate a11y","author":"soobing"},"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}},"next":{"id":"4cbf47cf-4a24-5ffd-88c3-bdede11e8a72","html":"

Docker 엔진 시작하기/종료하기

\n\n

컨테이너의 기본적인 사용 방법

\n\n

컨테이너 생성, 삭제, 실행, 정지

\n
docker run (옵션) 이미지 (인자)\ndocker stop 컨테이너_이름\ndocker rm 컨테이너_이름\ndocker ps -a
\n

예제

\n\n","frontmatter":{"date":"February 18, 2024","title":"docker에서 자주쓰는 명령어","categories":"infra","author":"soobing"},"fields":{"slug":"/infra/docker-command/"}},"prev":{"id":"e5b56e8b-108e-5455-8e4a-ebbfe2c18f09","html":"

프론트 개발을 하다보면 웹 페이지의 다양한 요소들을 직접 제어해야 하는 경우가 있습니다. 이 글에서는 CSSOM의 네 가지 주요 좌표 체계인 Offset, Page, Viewport, Screen에 대해 자세히 알아보고, 위치와 크기 관련 API는 어떤것이 있는지 살펴볼 예정입니다. 이 글을 통해 웹 페이지 내에서 요소의 위치와 크기를 정밀하게 제어하는 데 도움이 되길 바랍니다.

\n

표준 CSSOM 좌표 체계

\n

보통 클라이언트 개발에서 좌표의 원점은 왼쪽 하단이 아닌, 왼쪽 상단 입니다.

\n

CSS 객체 모델에서는 Offset, Page, Viewport, Screen 네 가지 표준 좌표 체계가 있습니다.

\n

\n \n \n

\n

출처: MDN Coordinate_systems

\n

Offset

\n\n

Page

\n\n

Viewport

\n\n

Screen

\n\n

참고: 상대적 위치 vs 절대적 위치

\n\n

브라우저 제공 API

\n
\n

💻 window 제공 API, 🌱 Element 제공 API

\n
\n

1. 브라우저 창 크기와 뷰포트

\n\n

2. 요소의 크기와 경계

\n\n

3. 스크롤 관련 정보

\n\n

4. 마우스 위치

\n\n

5. 스타일 정보

\n\n","frontmatter":{"date":"March 03, 2024","title":"브라우저 위치 및 크기 관련 API들","categories":"browser","author":"soobing"},"fields":{"slug":"/browser/browser-coordinate-size-api/"}},"site":{"siteMetadata":{"siteUrl":"https://soobing.github.io","comments":{"utterances":{"repo":"soobing/soobing.github.io"}}}}},"pageContext":{"slug":"/a11y/writing-css-with-accessibility-in-mind/","nextSlug":"/infra/docker-command/","prevSlug":"/browser/browser-coordinate-size-api/"}}, + "result": {"data":{"cur":{"id":"49246cb3-c4b8-5aeb-a4c5-a1b737e89b51","html":"
\n

원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939

\n
\n

CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다.

\n

이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다.

\n

읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다.

\n

약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리즈 중 세 번째 글입니다. 관심이 있다면 특별한 순서 없이, 접근성을 고려하여 HTML 작성하기접근성을 고려하여 자바스크립트 작성하기를 지금이나 나중에 읽어보면 좋습니다.

\n
\n

제 첫 웹사이트를 만든 것은 약 17년 전이었고, 그때는 CSS가 아직 상대적으로 새로운 것이었습니다. 그 이후로 많은 것이 변했고, CSS는 이제 우리에게 웹을 스타일링하기 위한 놀라운 도구를 제공합니다. 우리는 Verdana에서 웹폰트로, 고정된 너비에서 반응형 웹 디자인으로, 테이블 기반 레이아웃에서 그리드로 넘어갔고, 이제는 border와 font 또는 shadow에 이미지를 사용할 필요가 없습니다. 우리는 사용자 정의 속성, 쿼리, calc() 및 많은 새로운 단위들을 가지고 있습니다. 물론 이것은 지난 몇 년간의 훌륭한 발전들 중 일부에 불과합니다.

\n

\"\"

\n

접근성을 고려하여 CSS 작성하기

\n

CSS를 사용하여 문제를 해결하는 무한한 방법과 다양한 속성이 우리의 삶을 더 쉽게 만들어주지만, 동시에 사용자의 경험을 악화시킬 수도 있습니다. 사실, 단 세 줄의 CSS 만으로 웹사이트에 접근하기 어렵게 만들 수 있습니다.

\n

이 글에서는 접근성 있는 CSS를 작성하는 데 도움이 될만한 기술과 고려 사항 그리고 접근방식을 모두 모았습니다. 이 컬렉션은 기본 개념과 잘 알려진 속성으로 시작하여, 끝에는 좀 더 새로운 것들을 다룹니다.

\n

예상했던 것보다 많은 내용을 담게 되어, 가장 관심 있는 섹션으로 바로 이동할 수 있도록 링크가 걸린 목차를 마련했습니다.

\n\n

즐겁게 읽어주세요!

\n

🔗 가독성 있는 텍스트에서 읽기 쉬운 텍스트로

\n

이미지, 아이콘, 동영상은 오늘날 웹 디자인에서 빼놓을 수 없는 요소이지만, 여전히 거의 모든 웹사이트에서는 텍스트가 콘텐츠의 대부분을 차지합니다. 텍스트는 어떤 기기에서든 읽을 수 있어야 하기 때문에, 폰트 속성을 스타일링하고, 테스트하며, 미세 조정하는 데 상당한 시간을 할애하는 것이 중요합니다.

\n

글꼴 크기 확대

\n

\"\"

\n

사용자가 화면에서 떨어진 거리에 따라 글꼴 크기는 확대해야 합니다 (출처: Marvel)

\n

한때 12px 글꼴 크기가 본문(body) 텍스트의 표준이었지만, 해상도가 높은 기기의 등장으로 평균 글꼴 크기는 한동안 15에서 18px 사이에 정착했습니다. 최근 몇 년간, 글꼴 크기는 다시 20px 이상으로 상승했으며, 이는 좋은 일입니다. 텍스트는 스마트폰에서 충분히 커야 하며, TV와 같은 큰 화면에서 멀리서도 읽을 수 있도록 화면 크기에 따라 확대해야 합니다.

\n

서체의 특성이 매우 다양하기 때문에 표준의 최소 크기를 정의하는 것은 의미가 없지만, 작은 화면 크기에 좋은 시작점은 아마도 18-20px일 것입니다.

\n

물론 글꼴 크기에 대해 더 많이 말할 수 있지만, 이 글에서 다루기에는 너무 많습니다. 자세한 내용은 Christian Miller당신의 Body 텍스트는 너무 작습니다를 읽어보시길 권장합니다.

\n

라인 높이(line-height) 설정

\n

브라우저의 기본 라인 높이는 대략 **1.2**입니다. 웹 콘텐츠 접근성 지침에 따르면, 텍스트 블록 내의 문단에서는 최소 **1.5**여야 합니다.

\n

\"\"

\n

*line-height가 1.2인 문단과 1.5인 문단 비교*

\n

문단 내 라인 높이가 조정된 텍스트는 가독성이 향상될 뿐만 아니라, 시각적으로도 꽤 더 매력적입니다.

\n

텍스트를 왼쪽 또는 오른쪽으로 정렬

\n

\"\"

\n

양쪽 정렬된 텍스트의 불규칙한 단어 간격

\n

양쪽 정렬이 왼쪽 또는 오른쪽 정렬된 텍스트보다 보기 좋다고 생각하는 사람들도 있지만, 이는 나쁜 관행으로 간주됩니다. text-align: justify는 같은 길이의 줄을 만들기 위해 단어 간격을 조정합니다. 이러한 불균일한 공백은 가독성을 해칠 수 있으며 매우 산만해질 수 있습니다. 필요한 경우 단어를 구분하는 것도 해결책이 될 수 있지만, CSS 하이픈은 잘 지원되지 않고 예상대로 작동하지 않을 수 있습니다.

\n

문단 너비 정의

\n

여러 출처에 따르면 디자이너들은 줄당 45에서 85자를 유지해야 한다고 합니다. 이상적인 문단 너비는 65자라고 여겨집니다.

\n

텍스트 블록의 너비를 정의할 때 ch 단위가 유용할 수 있습니다. 1ch는 숫자 0을 나타내는 문자의 너비와 동일합니다. 또한, font-family 또는 font-size가 변경되면 이에 따라 변경됩니다.

\n
p {\n  /* 최대 너비 65자 */\n  max-width: 65ch;\n}
\n

어떠한 종류의 반응형 타이포그래피 기술을 사용한다면, 매우 큰 화면에서 사이트를 테스트해야 합니다. 글꼴 크기에 제한이 없다면, 특정 뷰포트 크기에서 텍스트가 읽기 어려워질 수 있습니다. 제한을 설정하는 방법이나 반응형 타이포그래피에 익숙하지 않다면, Mike Riethmullers의 글 반응형 타이포그래피에 대한 정밀한 제어를 읽어보시기 바랍니다.

\n

🔗 가상 요소에 콘텐츠 신중하게 사용하기

\n

우리는 ::before::after라는 가상 요소를 사용하여 요소의 맨 처음이나 맨 끝에 CSS를 추가할 수 있습니다. 이것은 디자인 요소를 우리 컴포넌트에 추가하는 매우 일반적이고 편리한 방법을 제공하지만, content 속성을 사용하여 내용을 추가하는 것도 가능합니다. 관심사의 분리의 관점에서 보면, 우리는 이렇게 하지 않아야 합니다.

\n
h2 {\n  content: "DON'T DO THIS";\n}
\n

우리의 내용은 HTML 파일, 데이터베이스 또는 API에서 오는 것이지, CSS에서 오는 것이 아닙니다. 때때로 우리는 폰트 아이콘 또는 특수 문자와 같은 텍스트가 아닌 콘텐츠를 추가하기 위해 content 속성을 사용합니다. 그렇게 할 때, 일부 스크린 리더가 생성된 콘텐츠를 인식하고 설명한다는 것을 기억해야 합니다. 생성된 콘텐츠가 순전히 표현적인 경우 보조 기술에서 숨겨야 합니다. 예를 들어 aria-hidden을 사용할 수 있습니다.

\n
<span class="icon icon-key" aria-hidden="true"></span>
\n

🔗 화면만이 유일한 매체가 아니다

\n

우리가 디지털 시대에 살고 있음에도 불구하고, 사람들은 여전히 물건을 인쇄합니다. 당신의 페이지가 인쇄되거나 PDF로 저장될 때도 접근성이 좋고 사용하기 쉬워야 합니다. 우리가 해야 할 일은 CSS에 @media 블록을 추가하여 종이에 어울리지 않거나 의미가 없는 요소(네비게이션 또는 광고)들을 숨기거나 스타일을 조정하는 것입니다.

\n
@media print {\n  .header {\n    position: static;\n  }  nav {\n    display: none;\n  }\n}
\n

인쇄된 웹 페이지의 문제 중 하나는 링크가 완전히 쓸모없다는 것입니다. 왜냐하면 그것들이 어디로 이끄는지 알 수 없기 때문입니다. 다행히도 CSS는 속성의 값들을 드러내고 화면(이 경우에는 종이)에 표시하는 방법을 제공합니다.

\n
@media print {\n  a[href^="http"]:not([href*="mywebsite.com"])::after {\n    content: " (" attr(href) ")";\n  }\n}
\n

위의 코드는 href 속성이 있고 http로 시작하지만 mywebsite.com이 값에 포함되지 않은 모든 링크 옆에 href 속성의 값을 표시합니다.

\n

Firefox와 특히 Chrome은 인쇄용 스타일 시트를 테스트하고 디버깅하기 위한 도구를 제공합니다.

\n

더 깊이 파고들고 싶다면, 인쇄 스타일 작업을 위한 팁과 트릭들을 모아놓은 것이 있습니다.

\n

🔗 완전히 지원되지 않는 속성 값에 대한 대안

\n

가끔 우리는 특정 속성 값을 사용하고 싶지만, 일부 브라우저에서 지원하지 않기 때문에 사용할 수 없는 상황에 처합니다. 하지만 대안을 제공하는 한, 그것을 사용하는 것을 멈출 필요는 없습니다. 종종 쿼리나 다른 탐지 기능을 사용하지 않고도 할 수 있습니다.

\n

예를 들어, IE와 이전 버전의 Edge가 이해하지 못하는 vmax 단위를 사용하고 싶다고 가정해 봅시다.

\n
div {\n  width: 50vmax; /* IE와 Edge 이전 버전에서 작동하지 않음 */\n}
\n

대안을 제공하기 위해서, 덜 이상적이지만 브라우저가 이해할 수 있는 width 속성을 사용하면 됩니다. 예를 들어 width: 50vw처럼. 다음 줄에서 실제 원하는 값을 설정합니다.

\n
div {\n  width: 50vw;\n  width: 50vmax;\n}
\n

vmax를 이해하지 못하는 브라우저는 width: 50vw를 해석하고 width: 50vmax는 무시합니다. 반면에 vmax를 이해하는 브라우저는 먼저 width: 50vw를 해석한 다음 width: 50vmax를 해석합니다. vmax 선언이 vw 선언 다음에 오기 때문에, 유저는 vmax 로 설정한 버전이 적용 됩니다.

\n

🔗 콘텐츠를 숨기는 여러 가지 방법

\n

HTML의 제목들은 문서(document)의 개요를 잡는 데 매우 유용합니다. <h1>부터 <h6>까지의 제목을 사용함으로써, 브라우저와 다른 소프트웨어에게 문서가 어떻게 구성되어있고 각 부분들이 어떻게 연관되어 있는지 알려줍니다. 문서 개요를 가지는 것은 매우 중요합니다, SEO에 좋고 스크린 리더 사용자들이 사이트를 탐색하는 데 도움이 됩니다. 디자인에 제목이 없어도 제목이 있으면 좋을 것 같은 경우가 발생할 수 있습니다. 그것은 종종 디자인 자체가 구조를 전달할 때의 경우입니다. 이런 경우에는 마크업에서 제목을 단순히 제거하지 않고 시각적으로 숨깁니다. CSS가 있든 없든 문서의 구조가 명확해야 합니다.

\n

이것은 물론 단 하나의 예일 뿐이며, 폼에서 라벨을 시각적으로 숨기는 것은 또 다른 예입니다(UX 관점에서 라벨을 숨기는 것은 바람직하지 않습니다).

\n

CSS에서는 콘텐츠를 숨기는 여러 가지 방법이 있으며, 적절한 기술을 올바른 시나리오에 맞게 선택하는 것은 여러분에게 달려 있습니다.

\n

모든 사람으로부터 콘텐츠 숨기기

\n

hidden 속성을 사용하거나 visibility: hidden 또는 display: none을 설정함으로써 콘텐츠를 완전히 숨길 수 있습니다. 사용자는 볼 수 없으며 스크린 리더나 검색 엔진도 읽을 수 없습니다.

\n

시각적으로만 콘텐츠 숨기기

\n

시각적으로만 콘텐츠를 숨기는 것은 간단하지 않습니다. 스크린 리더는 여전히 접근 가능하게 해야 하며, 브라우저의 특이점을 다뤄야 하고 요소가 포커스될 때 무슨 일이 발생하는지 결정해야 합니다. 물론, 이미 다른 사람들이 이를 해냈고 해결책이 있습니다.

\n

제가 연구를 해본 결과, 많은 다양한 접근 방식이 있다는 것을 알게 되었습니다. 그래서 전문가들에게 의견을 물어보았고, 추천된 기술을 분석하여 무슨 일이 일어나는지 완전히 이해했습니다.

\n
.visually-hidden {\n  /* 일반적인 흐름에서 항목을 제거 */\n  position: absolute;\n  /* 잘못 발음되거나 뭉개지는 텍스트를 위한 해결책 */\n  white-space: nowrap;\n  /* 가능한 가장 작은 크기로 설정 (일부 화면 낭독기는 높이와 너비가 0인 요소를 무시함) */\n  width: 1px;\n  height: 1px;\n  /* 크기 조정 후 넘치는 콘텐츠 숨기기 */\n  overflow: hidden;\n  /* 요소의 크기를 변경할 수 있는 모든 속성 초기화 */\n  border: 0;\n  padding: 0;\n  /* 클리핑은 요소의 어떤 부분이 표시될지 정의함. */\n  /* 구식 clip 속성은 구형 브라우저를 위함 */\n  clip: rect(0 0 0 0);\n  /* 최신 브라우저를 위한 clip-path. inset(50%)는 콘텐츠를 사라지게 하는 내부 사각형을 정의함. */\n  clip-path: inset(50%);\n  /* 현재 아무도 정확히 왜 margin: -1px이 있는지 확실하지 않음. 게다가 이것이 문제를 일으킬 수 있음 (참조:https://github.com/h5bp/html5-boilerplate/issues/1985). */\n  margin: -1px;\n}
\n

이 클래스를 어딘가에 저장하고 시각적으로 콘텐츠를 숨기면서 보조 기술과 검색 엔진은 접근 가능하게 하고 싶을 때 사용하세요.

\n

스킵 링크

\n

앞의 클래스들은 스킵 링크로 사용하기에도 적합합니다. 스킵 링크는 초기에는 시각적으로 숨겨져 있지만 포커스될 때 보이는 링크입니다. 스크린 리더와 키보드 사용자들이 소개 콘텐츠를 건너뛰고 주요 콘텐츠로 바로 이동할 수 있도록 페이지의 첫 번째 항목 중 하나여야 합니다. 기본적으로 사용자를 페이지의 특정 부분으로 이동시키는 앵커 링크입니다.

\n

\"\"

\n

“Skip to content” 링크는 포커스될 때 보입니다

\n

코드 펜에서 직접 시도해보고, Tab을 눌러 건너뛰기 링크를 드러내보세요.

\n

의미론적으로 콘텐츠 숨기기

\n

때때로 시각적으로 콘텐츠를 표시하되 스크린 리더에서는 숨기는 것이 의미가 있습니다. 예를 들어 아이콘을 사용할 때 그렇습니다. 그런 경우에는 숨기고자 하는 요소에 aria-hidden 속성을 추가하고 true로 설정하세요.

\n
<button>\n  <span class="icon icon-hamburger" aria-hidden="true"></span>\n  <span class="text">Menu</span>\n</button>
\n

기타

\n

콘텐츠를 숨기는 다른 방법들도 있습니다. 예를 들어 음수 text-indent나 제로 font-size 또는 height 등입니다. 일부는 작동하지만 특정 주의사항이 있습니다. 자세한 내용은 webaim.org텍스트 숨기기 기술을 읽어보세요.

\n

🔗 나쁜 대비는 신뢰할 수 없다

\n

우리의 디자인은 가독성을 위해 텍스트와 배경 사이에 충분한 대비를 제공해야 합니다. 저시력자뿐만 아니라 시각 장애가 없는 사람들도 고대비에서 이익을 얻습니다. 맑은 날에 스마트폰을 밖에서 사용하는 것을 생각해 보세요.

\n

색상 대비란 무엇이며 왜 중요한가

\n

세계보건기구(World Health Organization)에 따르면 인구의 약 4%가 시각 장애가 있습니다. 남성의 7~12%와 여성의 1% 미만이 어떤 형태의 색상 시력 결함을 가지고 있습니다. 이러한 장애 중 많은 것이 대비에 대한 민감도를 감소시키고, 일부 경우에는 색상을 구별하는 능력까지 줄입니다.

\n

두 색상이 색상 휠의 다른 부분에서 올 때, 그 색상들은 대비를 이룹니다. 일반적으로 말해서, 두 색상의 차이가 클수록 대비가 높습니다. 웹 디자이너 및 개발자로서 우리에게는 단순히 대비 자체뿐만 아니라, 텍스트에 적용됐을 때 얼마나 잘 작동하는지가 중요합니다. 텍스트와 그 배경 사이의 대비는 적어도 중등도 저시력을 가진 사람들이 읽을 수 있을 정도로 높아야 합니다. 물론, 이 기준을 충족하는지 고민할 필요는 없습니다. 웹 접근성 이니셔티브(Web Accessibility Initiative, WAI)는 이를 측정하기 위한 비율을 정의했습니다.

\n

최소 대비 비율

\n

대비 비율은 특정 배경에서 특정 크기와 너비를 가진 텍스트의 대비가 얼마나 높은지를 알려줍니다. 비율은 1:1에서 21:1까지 다양할 수 있습니다. 비교된 두 색상이 동일하면 1:1이고, 검정과 흰색이 대립되는 경우 21:1입니다.

\n

\"\"

\n

#777777 색상의 텍스트가 #DDDDDD 배경에서 3.3:1 비율을 가집니다. (출처: 대비 비율)

\n

웹 콘텐츠 접근성 지침(WCAG) 2.0에 따르면, 배경과 그 텍스트(또는 텍스트 이미지) 사이에 최소 4.5:1의 대비 비율이 존재해야 합니다. 이는 24px 미만(굵지 않은 경우) 및 19px 미만(굵은 경우)인 텍스트에 적용됩니다. 더 큰 텍스트의 경우 3:1 비율이면 충분합니다. 이것들은 레벨 AA 기준을 충족하기 위한 최소 수치입니다. 레벨 AAA를 통과하려면 일반 텍스트의 최소 비율은 7:1이고 굵은 텍스트는 4.5:1입니다. 규정 준수를 위한 필수 사항은 아니지만, 우리가 아이콘을 사용한다면 텍스트 대비 규정을 충족하는 아이콘을 사용해야 합니다.

\n

저는 제 친구 Daniel에게 비율에 대해 말했고, 우리가 현재 작업 중인 프로젝트에서 그것을 올바르게 가져가는 것이 중요하다고 말했습니다. 다양한 조합을 시도한 후, 그는 이것이 생각했던 것보다 어렵다고 전화로 말했습니다. 문제는 충분히 시각적으로 매력적인 조합이 없는 것이 아니라, 지난 몇 년 동안 디자이너들이 저대비 조합을 사용하는 데 익숙해졌다는 것입니다. 작은 에이전시뿐만 아니라 애플이나 구글과 같은 큰 회사들도 이 불리한 디자인 추세를 따르고 있습니다.

\n
\n

나이가 확실히 내 시력에 영향을 미쳤지만, 나는 디자인 트렌드로 고통받고 있다.

\n
\n

Kevin Marks

\n

대비 비율을 계산하기 위한 공식이 있지만, 오래된 계산기를 꺼내서 계산할 필요는 없습니다. 도구들이 있습니다.

\n

대비 비율 측정

\n

Chrome Canary에서는 개발자 도구에서 직접 대비 비율을 표시할 수 있습니다. Remy Sharp가 블로그 글에서 이를 공유합니다.

\n

\"\"

\n

Chrome 개발자 도구에서의 대비 비율.

\n

색상 대비와 일반적인 접근성을 테스트하기 위한 많은 도구들이 있습니다. 다음 목록은 광범위하지는 않지만, 제가 선호하는 도구들의 작은 모음집 입니다.

\n

온라인

\n\n

브라우저에서 빠르고 쉬운 대비 체커

\n\n

브라우저에서 좀 더 많은 옵션을 가진 대비 체커

\n\n

브라우저 도구로 대비와 더 많은 것들을 체크

\n\n

자동 대비 체크가 있는 색상 선택기

\n

브라우저 확장 프로그램과 개발자 도구

\n\n

Chrome 60은 Lighthouse를 기반으로 한 새로운 감사 패널과 함께 출시되었습니다. 접근성 점수를 부여하고 문제를 나열합니다.

\n\n

대비, 문서 개요 등을 테스트하기 위한 훌륭한 브라우저 확장 프로그램.

\n\n

aXe Chrome 확장 프로그램을 사용하여 웹 사이트에서 접근성 결함을 자동으로 찾는 도구.

\n

기타

\n\n

“WCAG에 대한 두 레이어의 색상 대비를 계산하는 Sketch 플러그인.

\n\n

고대비의 경험

\n

고대비의 색상을 사용하는 것도 훌륭하지만, 저시력을 가진 사람들은 여전히 웹사이트에서 사용하는 색상을 변경하고 싶어할 수 있습니다. 사용자의 요구는 매우 다양하며 그에 따라 색상 변경 방법도 다양합니다. 이 사실은 어느 정도 예측 불가능성을 내포하고 있으며, 우리의 페이지들이 항상 완전한 접근성을 보장하기 어렵게 만듭니다. 그래서 우리는 단지, 대비 수준 AA 또는 AAA 기준을 충족하는 것에만 의존해서는 안 되며, 웹사이트를 철저히 테스트하고 대비가 높은 대안을 제공하는 것도 고려해야 합니다.

\n

윈도우에서의 고대비 모드

\n

윈도우에서는 설정에서 고대비 옵션을 사용할 수 있습니다. 사용자는 자신만의 색상 설정을 정의하거나 사전 정의된 테마를 선택할 수 있습니다.

\n

\"\"

\n

윈도우에서의 고대비 설정

\n

간단한 로그인 폼을 만들었고(첫 번째 스크린샷 중 하나. https://dribbble.com/shots/1687064-Simple-Login-Form으로 부터 영감을 받음.) 고대비를 가진 다양한 테마로 테스트해 보았습니다.

\n

\"\"

\n

고대비 설정에서의 다양한 로그인 폼

\n

Anika Henke는 사용자들이 웹사이트에서 색상을 어떻게 변경하는지에 대해 썼습니다. 그녀는 폼을 테스트하던 중 입력 필드가 보이지 않게 되었고 버튼이 인식되지 않게 되었다는 것을 발견했습니다. 위 스크린샷에서도 같은 일이 발생하는 것을 볼 수 있습니다. 대체 텍스트가 없었다면, 사용자들은 두 개의 입력 필드가 있다는 것을 알지 못했을 것입니다. 인풋과 버튼에 기본 테두리를 추가하는 것은 빠른 해결책이었습니다(브라우저 간 테스트되지 않음).

\n

\"\"

\n

고대비 설정에서 인풋과 버튼에 테두리가 있는 개선된 로그인 폼

\n

미디어 쿼리를 사용하여 고대비 모드가 활성화되었는지 감지하고 특정 스타일을 제공할 수 있습니다.

\n
/* 고대비 모드 활성화 */\n@media (-ms-high-contrast:active) {\n}/* 특정 검정-백색 테마의 고대비 모드 */\n@media (-ms-high-contrast:black-on-white) {\n}/* 특정 검정-백색 테마의 고대비 모드 */\n@media (-ms-high-contrast:white-on-black) {\n}
\n

Patrick H. Lauke 는 윈도우 고대비 모드: -ms-high-contrast의 제한된 유용성에서 이 미디어 기능에 대한 그의 생각과 우려를 공유했습니다. 이에 응답으로 Greg Whitworth는 다음과 같이 지적했습니다. “이 기능의 유일한 목적은 대비 민감도를 가진 사용자들에게 더 나은 경험을 제공하는 것입니다. 그러므로, 특정 색상이 무엇인지에 대해 반드시 신경 쓸 필요는 없습니다. 어느 정도까지는, 여러분의 사이트가 어떻게 보이는지보다 어떻게 고대비에서 기능하는지에 대해 신경 써야 합니다.”

\n

높은 대비 크롬 확장 프로그램

\n

구글 크롬을 위한 고대비 확장 프로그램도 있어, 사용자들이 텍스트를 읽기 쉽게 만드는 여러 고대비 색상 필터로 웹을 탐색할 수 있습니다.

\n

고대비의 대안

\n

디자인의 일부분이 충분한 대비를 가지고 있지 않더라도, Alternate Version 조항을 사용하여 WCAG 기준을 충족할 수 있습니다. 이에 따르면, 사용자에게 페이지의 고대비 버전으로 연결하는 링크나, 페이지의 모든 측면이 준수하도록 페이지를 변경할 수 있는 페이지 상의 컨트롤을 제공해야 합니다.

\n

이 대안에 대한 몇 가지 기준이 있습니다.

\n\n

NoCoffee로 테스트하기

\n

\"\"

\n

NoCoffee는 저시력, 색상 결핍 및 차단된 시각 영역을 시뮬레이션합니다

\n

기준을 충족하는 것과 실제 사람을 대상으로 테스트하는 것은 별개의 문제입니다. 모든 사람이 전문적인 테스트 수단을 가지고 있는 것은 아닙니다. 다행히도, NoCoffee는 저시력, 색상 결핍 및 차단된 시각 영역을 쉽게 시뮬레이션할 수 있는 방법을 제공합니다. 이는 경미한~극심한 시력 문제를 가진 사람들이 직면하는 문제를 이해하는 데 도움이 될 수 있습니다.

\n

🔗 색상이 정보의 유일한 단서가 되어서는 안 된다

\n

앞서 언급했듯이, 많은 남성들이 시력 결함을 가지고 있습니다. 유형도 다양합니다. 가장 흔한 유형 중 하나인 중색 이상(Deuteranomaly)은 빨강과 녹색을 구분하기 어렵게 만듭니다. 색상 시력 결함이 있는 사람은 인터페이스를 사용할 수 없게 될 수 있으므로 색상만을 시각적 단서로 사용하는 것을 피해야 합니다.

\n

이전 예시에서 보여준 폼의 입력 필드에 성공 및 오류 상태를 나타내는 테두리를 추가했습니다. 다음 스크린샷은 색상만으로 사용자에게 충분한 피드백을 주지 못한다는 것을 보여줍니다. 테두리 색상이 전혀 보이지 않거나 잘못 보이는 경우가 있습니다.

\n

\"\"

\n

고대비 모드에서 색상만으로 폼의 성공과 실패를 구별하는 것은 효과적이지 않습니다.

\n

간단한 아이콘을 추가하면 접근성과 사용자 경험을 개선하는 데 도움이 될 수 있습니다.

\n

링크도 비슷한 예시 중 하나입니다. 링크는 색상만으로 일반 텍스트와 구별되어서는 안 됩니다. 링크에 밑줄을 유지하는 것이 좋습니다.

\n

🔗 순서에 신경 쓰기

\n

아이템들을 배치 순서를 바꾸는 방법은 많이 있습니다. 예를 들어, Flexbox에는 orderflex-direction이 있고, Grid에는 order, flex-auto-flow 및 명시적 배치가 있습니다. 이러한 속성들은 매우 유용하지만, 콘텐츠의 DOM 순서와 시각적 표현 사이의 연결이 끊어질 수 있습니다.

\n

다음 예시에서는 여러 그리드 속성을 사용하여 배치된 갤러리의 이미지를 볼 수 있습니다.

\n\n

처음에는 문제가 없어 보이지만, 키보드를 사용하여 이미지에서 이미지로 이동할 때 순서를 전혀 예측할 수 없다는 것을 알 수 있습니다. Tab 키를 눌렀을 때 다음에 어떤 이미지가 강조될지 알 수가 없습니다. 여기에 포커스 스타일이 없으면 최악의 상황이 될 수 있습니다.

\n\n

예측 불가능하거나 잘못된 순서는 키보드 사용자뿐만 아니라 스크린 리더 사용자에게도 문제가 됩니다. 스크린 리더는 DOM 순서대로 콘텐츠를 표시하므로 소프트웨어는 CSS 순서에 영향을 받지 않지만 사용자는 영향을 받습니다. 스크린 리더 사용자는 콘텐츠의 시각적 표현에 신경 쓰지 않는다고 생각할 수 있지만, 모든 스크린 리더의 사용자가 시각 장애가 있는 것은 아닙니다. 일부는 저시력이나 학습 장애가 있어 화면에 표시되는 내용을 보완하기 위해 스크린 리더를 사용합니다.

\n

이 순서 문제는 플렉스나 그리드 아이템뿐만 아니라 모든 종류의 위치 지정에도 적용됩니다. 스타일 없이도 의미가 있는 방식으로 콘텐츠를 정렬하는 것이 중요하며, 디자인에서의 순서와 일치하는지 확인해야 합니다. 일치하지 않으면 디자인을 다시 생각해야 할 수 있습니다. CSS에서 올바르게 위치시키지 못한다고 해서 마크업에서 요소들을 임의로 재배열하지 마세요.

\n

Rob Dodson’s의 콘텐츠 재배열이 접근성에 영향을 미치는가?와 Adrian Roselli의 코드의 순서가 중요하다 글을 참고해 더 자세한 내용을 알아보세요.

\n

🔗 중요한 것에 집중하기: focus

\n

저는 이미 키보드 탐색 기초와 포커스 가능한 요소들에 대해 접근성을 고려하여 자바스크립트 작성하기라는 글을 썼습니다. 이 주제가 처음이시라면, 그 글을 빠르게 읽고 오세요.

\n

키보드를 사용하여 웹사이트를 탐색할 수 있도록 하는 것은 중요합니다. 많은 사용자들이 웹 서핑을 할 때 키보드에 의존합니다. 그들 중에는 운동 장애가 있는 사람들, 시각 장애가 있는 사람들, 손이 없거나 어떤 이유로든 마우스나 트랙패드를 사용할 수 없는 사람들이 있습니다.

\n

CSS를 사용해서 포커스 가능한 요소들에 스타일을 적용할 수 있는 몇 가지 방법이 있습니다.

\n

포커스된 아이템 선택하기**

\n

:focus 의사 클래스를 사용하여 포커스 가능한 아이템들이 포커스 되었을때 스타일을 적용할 수 있습니다.

\n
a:focus {\n  background-color: #000000;\n  color: #FFFFFF;\n}
\n

기본 포커스 스타일은 브라우저마다 일관성이 없으며 보기 좋지 않은 경우가 많으며, 때로는 디자인과 잘 어울리지 않습니다. 사용자 경험을 개선하고 디자인에 맞는 맞춤 포커스 스타일을 제공하는 것이 좋습니다.

\n

하지만 무엇을 하든, 대체 스타일을 제공하지 않고 기본 윤곽선(점선 윤곽, 파란색 또는 주황색 반지)만 제거하지 마세요. 주로 키보드로 탐색하는 사용자들은 포커스 위치를 알 수 없다면 사이트를 사용할 수 없게 됩니다.

\n

\"\"

\n

대안 없이 기본 포커스 스타일을 제거하지 마세요 (출처: outlinenone.com)

\n

이것은 단순한 팁이 아니라 레벨 AA 기준 기준입니다.

\n

키보드와 마우스 사용자 구분하기

\n

이미 언급했듯이, 디자이너들이 좌절하는 것 중 하나는 브라우저 간 포커스 스타일의 일관성이 부족하다는 것입니다. 또한 마우스를 사용할 때도 일부 포커스 가능한 요소들에서 포커스 스타일이 보이는 점도 문제입니다. 때로는 마우스 사용자에게는 포커스 스타일을 보여줄 필요가 없으며, 심지어 방해가 되거나 미학적으로 불쾌할 수 있습니다.

\n

\"\"

\n

컨텐츠 영역이 클릭되었을 때 크롬에서 파란색 윤곽선이 보이는 맞춤 탭 컴포넌트 (출처: frend.co)

\n

웹 페이지의 특정 요소에 포커스가 가 있을 때 outline 속성을 제거해서는 안됩니다. 왜냐하면 컴포넌트가 더 이상 키보드 사용자에게 접근 불가능하기 때문입니다. 우리에게 필요한 것은 키보드와 마우스 사용을 구별하는 방법입니다. 이것은 CSS Level 4 선택자 명세의 일부인 :focus-ring 가상 클래스를 사용하여 가능 합니다. ”:focus-ring 가상 클래스는 요소가 :focus 가상 클래스와 일치하고, 사용자 에이전트(브라우저)가 휴리스틱(규칙이나 패턴)을 통해 요소에 특별히 표시되어야 함을 결정할 때 (일반적으로 ‘focus-ring’을 통해) 적용됩니다.” (출처: CSS 선택자 레벨 4 초안)

\n
/* 기본 윤곽선 제거 */\n:focus {\n  outline: none;\n}\n\n/* 윤곽선이 보여야 할 때만 추가 */\n:focus-ring {\n  outline: 2px solid blue;\n}
\n

아쉽게도 현재 어떤 브라우저도 :focus-ring의 표준 구현을 지원하지 않습니다. (Firefox는 -moz-focus-ring을 지원함), 하지만 적절할 때 .focus-ring 클래스를 추가하는 경량 폴리필이 있습니다.

\n
/* 자바스크립트가 활성화되어 작동하고, .focus-ring 클래스가 없는 \n모든 포커스 가능한 요소를 선택하여 윤곽선을 제거합니다 */\n.js-focus-ring :focus:not(.focus-ring) {\n    outline-width: 0;\n}
\n

자세한 내용은 Rob Dodson의 a11ycasts 에피소드, Focus Ring!을 시청하세요.

\n

포커스된 자식이 있는 요소에 대한 스타일링

\n

:focus-within는 상대적으로 새로운 가상 클래스로, 이미 대부분의 주요 브라우저에서 지원되고 있습니다. 이 클래스를 사용하면 현재 포커스된 자식 요소를 가진 요소를 선택할 수 있습니다.

\n

\"\"

\n

자식 항목 중 하나가 포커스되면 그림자가 표시되는 예시입니다.

\n
form:focus-within {\n  box-shadow: 0 0 4px 6px rgba(80,88,156,0.2);\n}
\n

이 기능을 CodePen에서 확인할 수 있습니다.

\n

포커스에 대한 자세한 내용은 YouTube에서 포커스란 무엇인가요? 영상을 참조하세요.

\n

🔗 그리드와 평평한 문서 구조

\n

새로운 사이트를 만들 때, 우리는 보통 HTML 작성부터 시작합니다. 적절한 마크업을 선택하고 요소들을 논리적 순서에 맞게 배치합니다. 문서가 올바르게 작성되어 있고 잘 구조화되어 있으며 순서가 의미 있게 되면 CSS를 추가합니다. CSS Grid Layout이 나오기 전에는, 특히 DOM 순서와 디자인 순서가 일치하지 않은 경우에 레이아웃을 만드는 것이 매우 까다로웠습니다. float, position 그리고 때때로 Flexbox조차도 어떤 상황에서는 충분히 유연하지 않았고, 우리는 DOM 순서를 변경하고 싶은 유혹에 빠지곤 했습니다. Grid의 명시적 배치와 해당 영역 덕분에 아이템을 위치시키는 데 필요한 모든 유연성을 갖게 되었습니다. 이것은 훌륭하지만, Grid는 문서 구조를 해칠 수 있는 새로운 유혹을 도입합니다.

\n

다음과 같은 디자인을 가지고 있고, 그 아이템들에 h2ul을 사용한다고 가정해 봅시다. 왜냐하면 그것이 당신에게 가장 의미가 있기 때문입니다.

\n

\"\"

\n

제목과 목록이 있는 레이아웃

\n
<div class="wrapper">\n  <h2>Heading</h2>\n  <ul>\n    <li><a href="#">Element 1</a></li>\n    <li><a href="#">Element 2</a></li>\n    <li><a href="#">Element 3</a></li>\n    <li><a href="#">Element 4</a></li>\n    <li><a href="#">Element 5</a></li>\n    <li><a href="#">Element 6</a></li>\n  </ul>\n</div>
\n

이러한 요소들을 열에 넣고 <h2>를 위치시키는 것은 꽤 쉽습니다… 또는 적어도 그렇게 보입니다.

\n
.wrapper {\n  display: grid;\n  grid-template-columns: 120px repeat(2, 1fr);\n  grid-gap: 20px;\n}h2 {\n  grid-column: 2 / -1;\n}
\n

\"\"

\n

제목과 목록이 있는 레이아웃. 그리드 컨테이너의 직접적인 자식만 그리드에 배치됩니다.

\n

하지만 예상과는 달리 보입니다. 문제는 그리드 컨테이너의 직접적인 자식만이 그리드에 배치된다는 것인데, 이 경우에는 <h2><ul>이 해당됩니다. 하지만 여러분은 <li>들을 그리드 아이템으로 만들고 싶습니다. 이 문제에 대한 최악의 해결책은 구조를 단순화시켜 <ul>을 제거하고 <li><div>로 변환하여 그리드 컨테이너의 직접적인 자식으로 만드는 것입니다.

\n

가장 좋은 해결책은 <ul>display 속성을 subgrid로 설정하는 것이지만, 불행히도 subgrid는 명세의 레벨 1에 포함되지 않았고 우리는 그것이 출시될 때까지 더 기다려야 합니다.

\n

<ul>display: contents를 사용할 수 있지만, 현재 Firefox만이 이를 지원합니다. display: contents는 요소의 자식들이 마치 요소의 부모의 직접적인 자식인 것처럼 보이게 만듭니다.

\n

결국, **<ul>**에 다른 그리드를 정의해야 합니다. 이것은 이상적이지 않지만, 문서의 구조를 단순화시키고 의미를 해치는 것보다는 낫습니다. 이것은 매우 기본적인 예제이며 목록이 전체 그리드를 차지하기 때문에 부모 그리드에서 일부 값을 상속받을 수 있습니다.

\n
ul {\n  /* 전체 그리드를 차지함 */\n  grid-column: 1 / -1;  /* 다른 그리드를 생성하고 부모 그리드에서 값을 상속받음 */\n  display: inherit;\n  grid-template-columns: inherit;\n  grid-gap: inherit;  /* display: contents를 이해하는 브라우저를 위해 display 덮어쓰기 */\n  display: contents;\n}
\n

CodePen에서 두 가지 솔루션을 볼 수 있습니다.

\n

결론

\n

이 글에서 꽤 많은 내용을 다루고 있지만 CSS와 접근성에 대해 알아야 할 모든 것을 다루지는 않습니다. 그러나 이것은 단순한 출발점 그 이상입니다. DOM 및 포커스 순서를 올바르게 설정하고 고대비에 신경 쓰고 일반적으로 접근성을 고려하여 디자인 하는 것만으로도 이미 훌륭한 작업을 하고 있는 것입니다. 새로운 페이지나 사이트를 만들 때마다 접근성을 약간만 더 고려한다면 웹을 더 나은 곳으로 만들 수 있습니다.

\n
\n

제약사항을 고려하여 디자인하는 것은 단순히 잘 디자인하는 것이다.

\n
\n

Aaron Gustafson

\n

이 글을 즐겁게 읽으셨고 새로운 것을 배우셨기를 바랍니다. 질문이나 피드백이 있으시면 댓글을 남겨주시거나 트위터를 통해 연락해 주시기 바랍니다.

\n

이 글을 작성하는 데 도움을 주신 멘토 Aaron Gustafson에게 감사드립니다.

\n

더 많은 접근성 팁

\n

이 글은 네 부분 시리즈 중 세 번째입니다. 마지막 글은 준비 중이며 곧 공개될 예정입니다.

\n
    \n
  1. 접근성을 고려한 HTML 작성하기
  2. \n
  3. 접근성을 고려한 JavaScript 작성하기
  4. \n
  5. 접근성을 고려한 CSS 작성하기
  6. \n
  7. 다음 글: 접근성을 고려한 디자인 및 개발 방법 배우기
  8. \n
\n

독자 여러분의 독서와 이 글을 좋아하시고 공유해 주시면 감사하겠습니다.

\n

제가 작성한 다른 글도 확인해보실 수 있습니다.

\n

Progressively Enhancing CSS Layout: From Floats To Flexbox To Grid

\n

The Difference Between Explicit and Implicit Grids

\n

추가적인 읽을 거리와 자료들

\n

가독성 있는 텍스트 작성법

\n\n

가상 요소에 콘텐츠 신중하게 사용하기

\n\n

화면만이 유일한 매체가 아니다

\n\n

완전히 지원되지 않는 속성 값에 대한 대안

\n\n

콘텐츠를 숨기는 여러 가지 방법

\n\n

나쁜 대비는 신뢰할 수 없다

\n\n

색상이 정보의 유일한 단서가 되어서는 안 된다

\n\n

순서에 신경 쓰기

\n\n

중요한 것에 집중하기: focus

\n\n

그리드와 평평한 문서 구조

\n\n","excerpt":"원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939 CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다. 이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다. 읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다. 약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리…","frontmatter":{"date":"February 18, 2024","title":"(번역) 접근성을 고려하여 CSS 작성하기","categories":"translate a11y","author":"soobing"},"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}},"next":{"id":"5fc01a99-7197-518c-a9de-60ace2976502","html":"
\n

원문: https://storybook.js.org/blog/storybook-react-server-components/

\n
\n
\n

스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용

\n
\n

리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다.

\n

가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다.

\n

이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 생깁니다.

\n

오늘은 이 질문에 대한 실험적인 답변으로 Next.js 프레임워크에서 스토리북의 RSC 지원을 공식적으로 발표하게 되어 기쁩니다. 이것은 완전히 클라이언트 측 구현이므로, 스토리북 애드온과 인테그레이션의 모든 생태계와 호환이 가능합니다.

\n

이것이 어떻게 작동하는지, 사용하는지, 그리고 지금 바로 사용해 볼 수 있는 방법에 대해 알아보고 싶다면 계속 읽어보세요!

\n

화성에서 온 서버, 금성에서 온 클라이언트

\n

RSC는 기존의 클라이언트 컴포넌트와 두 가지 주요 차이점이 있으며, 다음 예제에서 이에 대해 보여주고 있습니다.

\n
// ApiCard.tsx\n\nimport { ComponentProps } from 'react';\nimport { Card } from './Card';\nimport { findById } from './db';\n\nexport async function DbCard({ id }: {id: number}) {\n  let props;\n  try {\n    const contact = await findById(id);\n    props = { state: 'success', contact };\n  } catch (e) {\n    props = { state: 'error' };\n  }\n  return <Card {...props} />;\n}
\n
    \n
  1. 첫 번째 차이점은 컴포넌트가 async 라는 점인데, 이것은 클라이언트에서는 지원되지 않는 기능입니다.
  2. \n
  3. 두 번째 차이점은 컴포넌트가 Node 코드에 직접 액세스할 수 있다는 것인데, 위의 경우에서는 인증된 데이터베이스 연결을 래핑하는 findById 함수에 액세스하고 있습니다.
  4. \n
\n

RSC는 이러한 두 가지 차이점을 구현하기 위해 내부적으로 많은 작업을 수행합니다. 이 코드는 서버에서만 실행되며 클라이언트로 스트리밍되는 정적 JSON과 유사한 구조를 생성합니다.

\n

Storybook은 순수 클라이언트 애플리케이션입니다. Node가 전혀 없는 순수한 HTML/CSS/JS의 정적 빌드를 생성합니다! 따라서 RSC를 지원하려면 RSC를 클라이언트에서 렌더링하거나 Storybook을 서버용으로 다시 설계하는 방법을 찾아야 합니다.

\n

우리는 먼저 클라이언트 접근 방식에 중점을 두어 시작했습니다. 현재 아키텍처를 기반으로 수백 개의 애드온과 수백만 개의 스토리를 작성한 사용자에게 미치는 영향을 최소화하고 싶습니다.

\n

어떻게 클라이언트에서 동작하게 할 수 있었을까요?

\n

비동기로 처리하기

\n

RSC를 클라이언트에서 렌더링하는 데, 첫 번째 과제는 async 컴포넌트를 지원하는 방법을 알아내는 것입니다. 이미 Next.js의 canary 리액트 버전에서 비공식적으로 지원되고 있습니다. 이 간단한 솔루션에 기여한  JamesManningR 및 julRuss에게 특별한 감사를 전합니다!

\n
import { Suspense } from 'react';\n\nexport const ClientContact = ({ id }) => (\n  <Suspense><DbCard id={id} /></Suspense>\n);
\n

Storybook 8에서부터 @storybook/nextjs.storybook/main.js 파일에서 experimentalNextRSC 기능 플래그를 사용하여 스토리를 Suspense로 감싸줄 수 있습니다.

\n
// .storybook/main.js\nexport default {\n  features: {\n    experimentalNextRSC: true,\n  }\n};
\n

7.x 버전의 @storybook/nextjs에서도 RSC 스토리를 데코레이터로 래핑하여 수동으로 이 작업을 수행할 수도 있습니다.

\n

참고: 이 솔루션은 Next.js의 canary 버전 이외의 다른 Storybook React 프레임워크 (예: react-vitereact-webpack5)에서는 아직 작동하지 않습니다. 다음 버전에서는 이러한 제한이 해제되기를 바랍니다.

\n

모킹 및 로딩

\n

비동기 문제를 해결하는 것은 절반의 성공에 불과합니다. DbCard 컴포넌트는 데이터를 가져와 컴포넌트를 채우는 Node 코드를 참조합니다. 이것은 브라우저에서 Node 코드를 실행할 수 없는 문제가 있습니다!

\n

차선책으로 이 문제를 해결하기 위해서는, 깔끔한 데이터 액세스 계층 구축이 필요합니다. 이것은 또한 RSC의 설계자가 추천하는 최상의 권장 방법입니다.

\n

데이터 액세스 계층이 있으면, 브라우저에서 실행할 수 있도록 모킹할 수 있고 데이터를 정확하게 제어하여 다양한 UI 상태(로딩, 오류, 성공, 등)를 테스트할 수 있습니다.

\n

데이터 액세스 계층을 모킹하기 위해서는 Storybook에서 지원하는 모듈 모킹 또는 네트워크 모킹를 사용할 수 있습니다.

\n

모듈: jest.mock 스타일의 목을 제공하는 storybook-addon-module-mock 커뮤니티 애드온이 있습니다(웹팩 프로젝트에서만 지원). 더 간단하지만 더 제한적인 해결책으로 webpack/vite 별칭을 사용할 수도 있습니다. 향후 버전의 Storybook에서 모듈 모킹을 더 효율적으로 제공할 계획입니다.

\n

네트워크 API: 네트워크 요청을 모킹하기 위해 Mock Service Worker (msw)를 추천합니다. Storybook은 다양한 네트워크GraphQL 모킹 애드온을 지원합니다.

\n

우리의 예제로 돌아와서, storybook-addon-module-mock을 사용한 스토리는 다음과 같습니다.

\n
// DbCard.stories.js\n\nimport { StoryObj, Meta } from '@storybook/react';\nimport { createMock } from 'storybook-addon-module-mock';\n\nimport { DbCard } from './DbCard';\nimport * as db from './db';\n\nexport default { component: DbCard };\n\nexport const Success {\n  args: { id: 1 },\n  parameters: {\n    moduleMock: {\n      mock: () => {\n        const mock = createMock(db, 'findById');\n        mock.mockReturnValue(Promise.resolve({\n          name: 'Beyonce',\n          img: 'https://blackhistorywall.files.wordpress.com/2010/02/picture-device-independent-bitmap-119.jpg',\n          tel: '+123 456 789',\n          email: 'b@beyonce.com'\n        }))\n        return [mock];\n      },\n    },\n  },\n}
\n

API + 모듈 모킹의 전체 데모

\n

데이터베이스의 모듈 모킹 버전과 MSW2로 모킹된 API 버전을 포함한 위의 전체 예제에 대한 전체 내용은 우리의 전체 RSC 데모 Storybook 또는 GitHub 레포지토리를 확인하십시오.

\n

\"\"

\n

주의사항

\n

이 글에서는, Storybook에서 첫 번째 RSC에 대한 스토리를 성공적으로 작성했으며 이 모든 것이 내부적으로 어떻게 구현되었는지 보여주었습니다.

\n

이 모든 것은 꽤 간단한 과정이었지만, 이 접근 방식에는 제한 사항이 있습니다.

\n
    \n
  1. 일치성. 순수한 클라이언트 구현은 애플리케이션에서 실행되는 서버측 스트리밍 RSC 구현과 크게 다릅니다.
  2. \n
  3. 편의성. 여기서 제공하는 모킹 솔루션은 개선될 여지가 있습니다. 현재의 모듈 모킹 솔루션은 장황할 뿐만 아니라 Storybook의 args/controls와 잘 호환되지 않습니다.
  4. \n
\n

우리는 이러한 제한 사항을 향후에 해결할 계획이므로, 이 솔루션을 실험 단계로 표시했습니다.

\n

오늘부터 RSC를 위한 Storybook 사용하기 🎊

\n

RSC를 위한 Storybook을 사용하려면 Storybook을 8.0-alpha로 업그레이드하십시오.

\n
npx storybook@next upgrade --prerelease\n
\n

그런 다음 .storybook/main.ts에서 실험적 기능을 활성화하세요.

\n
// .storybook/main.js\nexport default {\n  features: {\n    experimentalNextRSC: true,\n  },\n};\n
\n

더 많은 정보를 원하시면, @storybook/nextjs README를 참조하세요.

\n

이것은 다음 메이져 버전인 Storybook 8.0의 내용을 자세히 설명하는 첫 번째 게시물이며, 앞으로 몇 달 안에 더 많은 내용이 추가될 예정입니다. 다음 릴리스에 대한 모든 소식을 확인하려면 소셜 미디어에서 팔로우하거나 Storybook 뉴스레터에 가입하세요!

\n","frontmatter":{"date":"February 02, 2024","title":"(번역) 리액트 서버 컴포넌트를 위한 스토리북","categories":"translate react","author":"soobing"},"fields":{"slug":"/react/storybook-react-server-components/"}},"prev":{"id":"4cbf47cf-4a24-5ffd-88c3-bdede11e8a72","html":"

Docker 엔진 시작하기/종료하기

\n\n

컨테이너의 기본적인 사용 방법

\n\n

컨테이너 생성, 삭제, 실행, 정지

\n
docker run (옵션) 이미지 (인자)\ndocker stop 컨테이너_이름\ndocker rm 컨테이너_이름\ndocker ps -a
\n

예제

\n\n","frontmatter":{"date":"February 18, 2024","title":"docker에서 자주쓰는 명령어","categories":"infra","author":"soobing"},"fields":{"slug":"/infra/docker-command/"}},"site":{"siteMetadata":{"siteUrl":"https://soobing.github.io","comments":{"utterances":{"repo":"soobing/soobing.github.io"}}}}},"pageContext":{"slug":"/a11y/writing-css-with-accessibility-in-mind/","nextSlug":"/react/storybook-react-server-components/","prevSlug":"/infra/docker-command/"}}, "staticQueryHashes": ["1073350324","2938748437"]} \ No newline at end of file diff --git a/page-data/browser/browser-coordinate-size-api/page-data.json b/page-data/browser/browser-coordinate-size-api/page-data.json index cbe0d76f..264f2b87 100644 --- a/page-data/browser/browser-coordinate-size-api/page-data.json +++ b/page-data/browser/browser-coordinate-size-api/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-blog-template-js", "path": "/browser/browser-coordinate-size-api/", - "result": {"data":{"cur":{"id":"e5b56e8b-108e-5455-8e4a-ebbfe2c18f09","html":"

프론트 개발을 하다보면 웹 페이지의 다양한 요소들을 직접 제어해야 하는 경우가 있습니다. 이 글에서는 CSSOM의 네 가지 주요 좌표 체계인 Offset, Page, Viewport, Screen에 대해 자세히 알아보고, 위치와 크기 관련 API는 어떤것이 있는지 살펴볼 예정입니다. 이 글을 통해 웹 페이지 내에서 요소의 위치와 크기를 정밀하게 제어하는 데 도움이 되길 바랍니다.

\n

표준 CSSOM 좌표 체계

\n

보통 클라이언트 개발에서 좌표의 원점은 왼쪽 하단이 아닌, 왼쪽 상단 입니다.

\n

CSS 객체 모델에서는 Offset, Page, Viewport, Screen 네 가지 표준 좌표 체계가 있습니다.

\n

\n \n \n

\n

출처: MDN Coordinate_systems

\n

Offset

\n\n

Page

\n\n

Viewport

\n\n

Screen

\n\n

참고: 상대적 위치 vs 절대적 위치

\n\n

브라우저 제공 API

\n
\n

💻 window 제공 API, 🌱 Element 제공 API

\n
\n

1. 브라우저 창 크기와 뷰포트

\n\n

2. 요소의 크기와 경계

\n\n

3. 스크롤 관련 정보

\n\n

4. 마우스 위치

\n\n

5. 스타일 정보

\n\n","excerpt":"프론트 개발을 하다보면 웹 페이지의 다양한 요소들을 직접 제어해야 하는 경우가 있습니다. 이 글에서는 CSSOM의 네 가지 주요 좌표 체계인 Offset, Page, Viewport, Screen에 대해 자세히 알아보고, 위치와 크기 관련 API는 어떤것이 있는지 살펴볼 예정입니다. 이 글을 통해 웹 페이지 내에서 요소의 위치와 크기를 정밀하게 제어하는 데 도움이 되길 바랍니다. 표준 CSSOM 좌표 체계 보통 클라이언트 개발에서 좌표의 원점은 왼쪽 하단이 아닌, 왼쪽 상단 입니다. CSS 객체 모델에서는 Offset, Page, Viewport, Screen 네 가지 표준 좌표 체계가 있습니다. 출처: MDN Coordinate_systems Offset 정의: 오프셋 좌표 체계는 특정 요소의 상대적 위치를 나타내는 데 사용됩니다. 이는 요소의 왼쪽 상단 모서리를 시작점(원점)으로 합니다. 특징: 이 좌표 체계는 요소의 부모 요소를 기준으로 한 상대적 위치를 제공합니다. 마…","frontmatter":{"date":"March 03, 2024","title":"브라우저 위치 및 크기 관련 API들","categories":"browser","author":"soobing"},"fields":{"slug":"/browser/browser-coordinate-size-api/"}},"next":{"id":"49246cb3-c4b8-5aeb-a4c5-a1b737e89b51","html":"
\n

원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939

\n
\n

CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다.

\n

이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다.

\n

읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다.

\n

약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리즈 중 세 번째 글입니다. 관심이 있다면 특별한 순서 없이, 접근성을 고려하여 HTML 작성하기접근성을 고려하여 자바스크립트 작성하기를 지금이나 나중에 읽어보면 좋습니다.

\n
\n

제 첫 웹사이트를 만든 것은 약 17년 전이었고, 그때는 CSS가 아직 상대적으로 새로운 것이었습니다. 그 이후로 많은 것이 변했고, CSS는 이제 우리에게 웹을 스타일링하기 위한 놀라운 도구를 제공합니다. 우리는 Verdana에서 웹폰트로, 고정된 너비에서 반응형 웹 디자인으로, 테이블 기반 레이아웃에서 그리드로 넘어갔고, 이제는 border와 font 또는 shadow에 이미지를 사용할 필요가 없습니다. 우리는 사용자 정의 속성, 쿼리, calc() 및 많은 새로운 단위들을 가지고 있습니다. 물론 이것은 지난 몇 년간의 훌륭한 발전들 중 일부에 불과합니다.

\n

\"\"

\n

접근성을 고려하여 CSS 작성하기

\n

CSS를 사용하여 문제를 해결하는 무한한 방법과 다양한 속성이 우리의 삶을 더 쉽게 만들어주지만, 동시에 사용자의 경험을 악화시킬 수도 있습니다. 사실, 단 세 줄의 CSS 만으로 웹사이트에 접근하기 어렵게 만들 수 있습니다.

\n

이 글에서는 접근성 있는 CSS를 작성하는 데 도움이 될만한 기술과 고려 사항 그리고 접근방식을 모두 모았습니다. 이 컬렉션은 기본 개념과 잘 알려진 속성으로 시작하여, 끝에는 좀 더 새로운 것들을 다룹니다.

\n

예상했던 것보다 많은 내용을 담게 되어, 가장 관심 있는 섹션으로 바로 이동할 수 있도록 링크가 걸린 목차를 마련했습니다.

\n\n

즐겁게 읽어주세요!

\n

🔗 가독성 있는 텍스트에서 읽기 쉬운 텍스트로

\n

이미지, 아이콘, 동영상은 오늘날 웹 디자인에서 빼놓을 수 없는 요소이지만, 여전히 거의 모든 웹사이트에서는 텍스트가 콘텐츠의 대부분을 차지합니다. 텍스트는 어떤 기기에서든 읽을 수 있어야 하기 때문에, 폰트 속성을 스타일링하고, 테스트하며, 미세 조정하는 데 상당한 시간을 할애하는 것이 중요합니다.

\n

글꼴 크기 확대

\n

\"\"

\n

사용자가 화면에서 떨어진 거리에 따라 글꼴 크기는 확대해야 합니다 (출처: Marvel)

\n

한때 12px 글꼴 크기가 본문(body) 텍스트의 표준이었지만, 해상도가 높은 기기의 등장으로 평균 글꼴 크기는 한동안 15에서 18px 사이에 정착했습니다. 최근 몇 년간, 글꼴 크기는 다시 20px 이상으로 상승했으며, 이는 좋은 일입니다. 텍스트는 스마트폰에서 충분히 커야 하며, TV와 같은 큰 화면에서 멀리서도 읽을 수 있도록 화면 크기에 따라 확대해야 합니다.

\n

서체의 특성이 매우 다양하기 때문에 표준의 최소 크기를 정의하는 것은 의미가 없지만, 작은 화면 크기에 좋은 시작점은 아마도 18-20px일 것입니다.

\n

물론 글꼴 크기에 대해 더 많이 말할 수 있지만, 이 글에서 다루기에는 너무 많습니다. 자세한 내용은 Christian Miller당신의 Body 텍스트는 너무 작습니다를 읽어보시길 권장합니다.

\n

라인 높이(line-height) 설정

\n

브라우저의 기본 라인 높이는 대략 **1.2**입니다. 웹 콘텐츠 접근성 지침에 따르면, 텍스트 블록 내의 문단에서는 최소 **1.5**여야 합니다.

\n

\"\"

\n

*line-height가 1.2인 문단과 1.5인 문단 비교*

\n

문단 내 라인 높이가 조정된 텍스트는 가독성이 향상될 뿐만 아니라, 시각적으로도 꽤 더 매력적입니다.

\n

텍스트를 왼쪽 또는 오른쪽으로 정렬

\n

\"\"

\n

양쪽 정렬된 텍스트의 불규칙한 단어 간격

\n

양쪽 정렬이 왼쪽 또는 오른쪽 정렬된 텍스트보다 보기 좋다고 생각하는 사람들도 있지만, 이는 나쁜 관행으로 간주됩니다. text-align: justify는 같은 길이의 줄을 만들기 위해 단어 간격을 조정합니다. 이러한 불균일한 공백은 가독성을 해칠 수 있으며 매우 산만해질 수 있습니다. 필요한 경우 단어를 구분하는 것도 해결책이 될 수 있지만, CSS 하이픈은 잘 지원되지 않고 예상대로 작동하지 않을 수 있습니다.

\n

문단 너비 정의

\n

여러 출처에 따르면 디자이너들은 줄당 45에서 85자를 유지해야 한다고 합니다. 이상적인 문단 너비는 65자라고 여겨집니다.

\n

텍스트 블록의 너비를 정의할 때 ch 단위가 유용할 수 있습니다. 1ch는 숫자 0을 나타내는 문자의 너비와 동일합니다. 또한, font-family 또는 font-size가 변경되면 이에 따라 변경됩니다.

\n
p {\n  /* 최대 너비 65자 */\n  max-width: 65ch;\n}
\n

어떠한 종류의 반응형 타이포그래피 기술을 사용한다면, 매우 큰 화면에서 사이트를 테스트해야 합니다. 글꼴 크기에 제한이 없다면, 특정 뷰포트 크기에서 텍스트가 읽기 어려워질 수 있습니다. 제한을 설정하는 방법이나 반응형 타이포그래피에 익숙하지 않다면, Mike Riethmullers의 글 반응형 타이포그래피에 대한 정밀한 제어를 읽어보시기 바랍니다.

\n

🔗 가상 요소에 콘텐츠 신중하게 사용하기

\n

우리는 ::before::after라는 가상 요소를 사용하여 요소의 맨 처음이나 맨 끝에 CSS를 추가할 수 있습니다. 이것은 디자인 요소를 우리 컴포넌트에 추가하는 매우 일반적이고 편리한 방법을 제공하지만, content 속성을 사용하여 내용을 추가하는 것도 가능합니다. 관심사의 분리의 관점에서 보면, 우리는 이렇게 하지 않아야 합니다.

\n
h2 {\n  content: "DON'T DO THIS";\n}
\n

우리의 내용은 HTML 파일, 데이터베이스 또는 API에서 오는 것이지, CSS에서 오는 것이 아닙니다. 때때로 우리는 폰트 아이콘 또는 특수 문자와 같은 텍스트가 아닌 콘텐츠를 추가하기 위해 content 속성을 사용합니다. 그렇게 할 때, 일부 스크린 리더가 생성된 콘텐츠를 인식하고 설명한다는 것을 기억해야 합니다. 생성된 콘텐츠가 순전히 표현적인 경우 보조 기술에서 숨겨야 합니다. 예를 들어 aria-hidden을 사용할 수 있습니다.

\n
<span class="icon icon-key" aria-hidden="true"></span>
\n

🔗 화면만이 유일한 매체가 아니다

\n

우리가 디지털 시대에 살고 있음에도 불구하고, 사람들은 여전히 물건을 인쇄합니다. 당신의 페이지가 인쇄되거나 PDF로 저장될 때도 접근성이 좋고 사용하기 쉬워야 합니다. 우리가 해야 할 일은 CSS에 @media 블록을 추가하여 종이에 어울리지 않거나 의미가 없는 요소(네비게이션 또는 광고)들을 숨기거나 스타일을 조정하는 것입니다.

\n
@media print {\n  .header {\n    position: static;\n  }  nav {\n    display: none;\n  }\n}
\n

인쇄된 웹 페이지의 문제 중 하나는 링크가 완전히 쓸모없다는 것입니다. 왜냐하면 그것들이 어디로 이끄는지 알 수 없기 때문입니다. 다행히도 CSS는 속성의 값들을 드러내고 화면(이 경우에는 종이)에 표시하는 방법을 제공합니다.

\n
@media print {\n  a[href^="http"]:not([href*="mywebsite.com"])::after {\n    content: " (" attr(href) ")";\n  }\n}
\n

위의 코드는 href 속성이 있고 http로 시작하지만 mywebsite.com이 값에 포함되지 않은 모든 링크 옆에 href 속성의 값을 표시합니다.

\n

Firefox와 특히 Chrome은 인쇄용 스타일 시트를 테스트하고 디버깅하기 위한 도구를 제공합니다.

\n

더 깊이 파고들고 싶다면, 인쇄 스타일 작업을 위한 팁과 트릭들을 모아놓은 것이 있습니다.

\n

🔗 완전히 지원되지 않는 속성 값에 대한 대안

\n

가끔 우리는 특정 속성 값을 사용하고 싶지만, 일부 브라우저에서 지원하지 않기 때문에 사용할 수 없는 상황에 처합니다. 하지만 대안을 제공하는 한, 그것을 사용하는 것을 멈출 필요는 없습니다. 종종 쿼리나 다른 탐지 기능을 사용하지 않고도 할 수 있습니다.

\n

예를 들어, IE와 이전 버전의 Edge가 이해하지 못하는 vmax 단위를 사용하고 싶다고 가정해 봅시다.

\n
div {\n  width: 50vmax; /* IE와 Edge 이전 버전에서 작동하지 않음 */\n}
\n

대안을 제공하기 위해서, 덜 이상적이지만 브라우저가 이해할 수 있는 width 속성을 사용하면 됩니다. 예를 들어 width: 50vw처럼. 다음 줄에서 실제 원하는 값을 설정합니다.

\n
div {\n  width: 50vw;\n  width: 50vmax;\n}
\n

vmax를 이해하지 못하는 브라우저는 width: 50vw를 해석하고 width: 50vmax는 무시합니다. 반면에 vmax를 이해하는 브라우저는 먼저 width: 50vw를 해석한 다음 width: 50vmax를 해석합니다. vmax 선언이 vw 선언 다음에 오기 때문에, 유저는 vmax 로 설정한 버전이 적용 됩니다.

\n

🔗 콘텐츠를 숨기는 여러 가지 방법

\n

HTML의 제목들은 문서(document)의 개요를 잡는 데 매우 유용합니다. <h1>부터 <h6>까지의 제목을 사용함으로써, 브라우저와 다른 소프트웨어에게 문서가 어떻게 구성되어있고 각 부분들이 어떻게 연관되어 있는지 알려줍니다. 문서 개요를 가지는 것은 매우 중요합니다, SEO에 좋고 스크린 리더 사용자들이 사이트를 탐색하는 데 도움이 됩니다. 디자인에 제목이 없어도 제목이 있으면 좋을 것 같은 경우가 발생할 수 있습니다. 그것은 종종 디자인 자체가 구조를 전달할 때의 경우입니다. 이런 경우에는 마크업에서 제목을 단순히 제거하지 않고 시각적으로 숨깁니다. CSS가 있든 없든 문서의 구조가 명확해야 합니다.

\n

이것은 물론 단 하나의 예일 뿐이며, 폼에서 라벨을 시각적으로 숨기는 것은 또 다른 예입니다(UX 관점에서 라벨을 숨기는 것은 바람직하지 않습니다).

\n

CSS에서는 콘텐츠를 숨기는 여러 가지 방법이 있으며, 적절한 기술을 올바른 시나리오에 맞게 선택하는 것은 여러분에게 달려 있습니다.

\n

모든 사람으로부터 콘텐츠 숨기기

\n

hidden 속성을 사용하거나 visibility: hidden 또는 display: none을 설정함으로써 콘텐츠를 완전히 숨길 수 있습니다. 사용자는 볼 수 없으며 스크린 리더나 검색 엔진도 읽을 수 없습니다.

\n

시각적으로만 콘텐츠 숨기기

\n

시각적으로만 콘텐츠를 숨기는 것은 간단하지 않습니다. 스크린 리더는 여전히 접근 가능하게 해야 하며, 브라우저의 특이점을 다뤄야 하고 요소가 포커스될 때 무슨 일이 발생하는지 결정해야 합니다. 물론, 이미 다른 사람들이 이를 해냈고 해결책이 있습니다.

\n

제가 연구를 해본 결과, 많은 다양한 접근 방식이 있다는 것을 알게 되었습니다. 그래서 전문가들에게 의견을 물어보았고, 추천된 기술을 분석하여 무슨 일이 일어나는지 완전히 이해했습니다.

\n
.visually-hidden {\n  /* 일반적인 흐름에서 항목을 제거 */\n  position: absolute;\n  /* 잘못 발음되거나 뭉개지는 텍스트를 위한 해결책 */\n  white-space: nowrap;\n  /* 가능한 가장 작은 크기로 설정 (일부 화면 낭독기는 높이와 너비가 0인 요소를 무시함) */\n  width: 1px;\n  height: 1px;\n  /* 크기 조정 후 넘치는 콘텐츠 숨기기 */\n  overflow: hidden;\n  /* 요소의 크기를 변경할 수 있는 모든 속성 초기화 */\n  border: 0;\n  padding: 0;\n  /* 클리핑은 요소의 어떤 부분이 표시될지 정의함. */\n  /* 구식 clip 속성은 구형 브라우저를 위함 */\n  clip: rect(0 0 0 0);\n  /* 최신 브라우저를 위한 clip-path. inset(50%)는 콘텐츠를 사라지게 하는 내부 사각형을 정의함. */\n  clip-path: inset(50%);\n  /* 현재 아무도 정확히 왜 margin: -1px이 있는지 확실하지 않음. 게다가 이것이 문제를 일으킬 수 있음 (참조:https://github.com/h5bp/html5-boilerplate/issues/1985). */\n  margin: -1px;\n}
\n

이 클래스를 어딘가에 저장하고 시각적으로 콘텐츠를 숨기면서 보조 기술과 검색 엔진은 접근 가능하게 하고 싶을 때 사용하세요.

\n

스킵 링크

\n

앞의 클래스들은 스킵 링크로 사용하기에도 적합합니다. 스킵 링크는 초기에는 시각적으로 숨겨져 있지만 포커스될 때 보이는 링크입니다. 스크린 리더와 키보드 사용자들이 소개 콘텐츠를 건너뛰고 주요 콘텐츠로 바로 이동할 수 있도록 페이지의 첫 번째 항목 중 하나여야 합니다. 기본적으로 사용자를 페이지의 특정 부분으로 이동시키는 앵커 링크입니다.

\n

\"\"

\n

“Skip to content” 링크는 포커스될 때 보입니다

\n

코드 펜에서 직접 시도해보고, Tab을 눌러 건너뛰기 링크를 드러내보세요.

\n

의미론적으로 콘텐츠 숨기기

\n

때때로 시각적으로 콘텐츠를 표시하되 스크린 리더에서는 숨기는 것이 의미가 있습니다. 예를 들어 아이콘을 사용할 때 그렇습니다. 그런 경우에는 숨기고자 하는 요소에 aria-hidden 속성을 추가하고 true로 설정하세요.

\n
<button>\n  <span class="icon icon-hamburger" aria-hidden="true"></span>\n  <span class="text">Menu</span>\n</button>
\n

기타

\n

콘텐츠를 숨기는 다른 방법들도 있습니다. 예를 들어 음수 text-indent나 제로 font-size 또는 height 등입니다. 일부는 작동하지만 특정 주의사항이 있습니다. 자세한 내용은 webaim.org텍스트 숨기기 기술을 읽어보세요.

\n

🔗 나쁜 대비는 신뢰할 수 없다

\n

우리의 디자인은 가독성을 위해 텍스트와 배경 사이에 충분한 대비를 제공해야 합니다. 저시력자뿐만 아니라 시각 장애가 없는 사람들도 고대비에서 이익을 얻습니다. 맑은 날에 스마트폰을 밖에서 사용하는 것을 생각해 보세요.

\n

색상 대비란 무엇이며 왜 중요한가

\n

세계보건기구(World Health Organization)에 따르면 인구의 약 4%가 시각 장애가 있습니다. 남성의 7~12%와 여성의 1% 미만이 어떤 형태의 색상 시력 결함을 가지고 있습니다. 이러한 장애 중 많은 것이 대비에 대한 민감도를 감소시키고, 일부 경우에는 색상을 구별하는 능력까지 줄입니다.

\n

두 색상이 색상 휠의 다른 부분에서 올 때, 그 색상들은 대비를 이룹니다. 일반적으로 말해서, 두 색상의 차이가 클수록 대비가 높습니다. 웹 디자이너 및 개발자로서 우리에게는 단순히 대비 자체뿐만 아니라, 텍스트에 적용됐을 때 얼마나 잘 작동하는지가 중요합니다. 텍스트와 그 배경 사이의 대비는 적어도 중등도 저시력을 가진 사람들이 읽을 수 있을 정도로 높아야 합니다. 물론, 이 기준을 충족하는지 고민할 필요는 없습니다. 웹 접근성 이니셔티브(Web Accessibility Initiative, WAI)는 이를 측정하기 위한 비율을 정의했습니다.

\n

최소 대비 비율

\n

대비 비율은 특정 배경에서 특정 크기와 너비를 가진 텍스트의 대비가 얼마나 높은지를 알려줍니다. 비율은 1:1에서 21:1까지 다양할 수 있습니다. 비교된 두 색상이 동일하면 1:1이고, 검정과 흰색이 대립되는 경우 21:1입니다.

\n

\"\"

\n

#777777 색상의 텍스트가 #DDDDDD 배경에서 3.3:1 비율을 가집니다. (출처: 대비 비율)

\n

웹 콘텐츠 접근성 지침(WCAG) 2.0에 따르면, 배경과 그 텍스트(또는 텍스트 이미지) 사이에 최소 4.5:1의 대비 비율이 존재해야 합니다. 이는 24px 미만(굵지 않은 경우) 및 19px 미만(굵은 경우)인 텍스트에 적용됩니다. 더 큰 텍스트의 경우 3:1 비율이면 충분합니다. 이것들은 레벨 AA 기준을 충족하기 위한 최소 수치입니다. 레벨 AAA를 통과하려면 일반 텍스트의 최소 비율은 7:1이고 굵은 텍스트는 4.5:1입니다. 규정 준수를 위한 필수 사항은 아니지만, 우리가 아이콘을 사용한다면 텍스트 대비 규정을 충족하는 아이콘을 사용해야 합니다.

\n

저는 제 친구 Daniel에게 비율에 대해 말했고, 우리가 현재 작업 중인 프로젝트에서 그것을 올바르게 가져가는 것이 중요하다고 말했습니다. 다양한 조합을 시도한 후, 그는 이것이 생각했던 것보다 어렵다고 전화로 말했습니다. 문제는 충분히 시각적으로 매력적인 조합이 없는 것이 아니라, 지난 몇 년 동안 디자이너들이 저대비 조합을 사용하는 데 익숙해졌다는 것입니다. 작은 에이전시뿐만 아니라 애플이나 구글과 같은 큰 회사들도 이 불리한 디자인 추세를 따르고 있습니다.

\n
\n

나이가 확실히 내 시력에 영향을 미쳤지만, 나는 디자인 트렌드로 고통받고 있다.

\n
\n

Kevin Marks

\n

대비 비율을 계산하기 위한 공식이 있지만, 오래된 계산기를 꺼내서 계산할 필요는 없습니다. 도구들이 있습니다.

\n

대비 비율 측정

\n

Chrome Canary에서는 개발자 도구에서 직접 대비 비율을 표시할 수 있습니다. Remy Sharp가 블로그 글에서 이를 공유합니다.

\n

\"\"

\n

Chrome 개발자 도구에서의 대비 비율.

\n

색상 대비와 일반적인 접근성을 테스트하기 위한 많은 도구들이 있습니다. 다음 목록은 광범위하지는 않지만, 제가 선호하는 도구들의 작은 모음집 입니다.

\n

온라인

\n\n

브라우저에서 빠르고 쉬운 대비 체커

\n\n

브라우저에서 좀 더 많은 옵션을 가진 대비 체커

\n\n

브라우저 도구로 대비와 더 많은 것들을 체크

\n\n

자동 대비 체크가 있는 색상 선택기

\n

브라우저 확장 프로그램과 개발자 도구

\n\n

Chrome 60은 Lighthouse를 기반으로 한 새로운 감사 패널과 함께 출시되었습니다. 접근성 점수를 부여하고 문제를 나열합니다.

\n\n

대비, 문서 개요 등을 테스트하기 위한 훌륭한 브라우저 확장 프로그램.

\n\n

aXe Chrome 확장 프로그램을 사용하여 웹 사이트에서 접근성 결함을 자동으로 찾는 도구.

\n

기타

\n\n

“WCAG에 대한 두 레이어의 색상 대비를 계산하는 Sketch 플러그인.

\n\n

고대비의 경험

\n

고대비의 색상을 사용하는 것도 훌륭하지만, 저시력을 가진 사람들은 여전히 웹사이트에서 사용하는 색상을 변경하고 싶어할 수 있습니다. 사용자의 요구는 매우 다양하며 그에 따라 색상 변경 방법도 다양합니다. 이 사실은 어느 정도 예측 불가능성을 내포하고 있으며, 우리의 페이지들이 항상 완전한 접근성을 보장하기 어렵게 만듭니다. 그래서 우리는 단지, 대비 수준 AA 또는 AAA 기준을 충족하는 것에만 의존해서는 안 되며, 웹사이트를 철저히 테스트하고 대비가 높은 대안을 제공하는 것도 고려해야 합니다.

\n

윈도우에서의 고대비 모드

\n

윈도우에서는 설정에서 고대비 옵션을 사용할 수 있습니다. 사용자는 자신만의 색상 설정을 정의하거나 사전 정의된 테마를 선택할 수 있습니다.

\n

\"\"

\n

윈도우에서의 고대비 설정

\n

간단한 로그인 폼을 만들었고(첫 번째 스크린샷 중 하나. https://dribbble.com/shots/1687064-Simple-Login-Form으로 부터 영감을 받음.) 고대비를 가진 다양한 테마로 테스트해 보았습니다.

\n

\"\"

\n

고대비 설정에서의 다양한 로그인 폼

\n

Anika Henke는 사용자들이 웹사이트에서 색상을 어떻게 변경하는지에 대해 썼습니다. 그녀는 폼을 테스트하던 중 입력 필드가 보이지 않게 되었고 버튼이 인식되지 않게 되었다는 것을 발견했습니다. 위 스크린샷에서도 같은 일이 발생하는 것을 볼 수 있습니다. 대체 텍스트가 없었다면, 사용자들은 두 개의 입력 필드가 있다는 것을 알지 못했을 것입니다. 인풋과 버튼에 기본 테두리를 추가하는 것은 빠른 해결책이었습니다(브라우저 간 테스트되지 않음).

\n

\"\"

\n

고대비 설정에서 인풋과 버튼에 테두리가 있는 개선된 로그인 폼

\n

미디어 쿼리를 사용하여 고대비 모드가 활성화되었는지 감지하고 특정 스타일을 제공할 수 있습니다.

\n
/* 고대비 모드 활성화 */\n@media (-ms-high-contrast:active) {\n}/* 특정 검정-백색 테마의 고대비 모드 */\n@media (-ms-high-contrast:black-on-white) {\n}/* 특정 검정-백색 테마의 고대비 모드 */\n@media (-ms-high-contrast:white-on-black) {\n}
\n

Patrick H. Lauke 는 윈도우 고대비 모드: -ms-high-contrast의 제한된 유용성에서 이 미디어 기능에 대한 그의 생각과 우려를 공유했습니다. 이에 응답으로 Greg Whitworth는 다음과 같이 지적했습니다. “이 기능의 유일한 목적은 대비 민감도를 가진 사용자들에게 더 나은 경험을 제공하는 것입니다. 그러므로, 특정 색상이 무엇인지에 대해 반드시 신경 쓸 필요는 없습니다. 어느 정도까지는, 여러분의 사이트가 어떻게 보이는지보다 어떻게 고대비에서 기능하는지에 대해 신경 써야 합니다.”

\n

높은 대비 크롬 확장 프로그램

\n

구글 크롬을 위한 고대비 확장 프로그램도 있어, 사용자들이 텍스트를 읽기 쉽게 만드는 여러 고대비 색상 필터로 웹을 탐색할 수 있습니다.

\n

고대비의 대안

\n

디자인의 일부분이 충분한 대비를 가지고 있지 않더라도, Alternate Version 조항을 사용하여 WCAG 기준을 충족할 수 있습니다. 이에 따르면, 사용자에게 페이지의 고대비 버전으로 연결하는 링크나, 페이지의 모든 측면이 준수하도록 페이지를 변경할 수 있는 페이지 상의 컨트롤을 제공해야 합니다.

\n

이 대안에 대한 몇 가지 기준이 있습니다.

\n\n

NoCoffee로 테스트하기

\n

\"\"

\n

NoCoffee는 저시력, 색상 결핍 및 차단된 시각 영역을 시뮬레이션합니다

\n

기준을 충족하는 것과 실제 사람을 대상으로 테스트하는 것은 별개의 문제입니다. 모든 사람이 전문적인 테스트 수단을 가지고 있는 것은 아닙니다. 다행히도, NoCoffee는 저시력, 색상 결핍 및 차단된 시각 영역을 쉽게 시뮬레이션할 수 있는 방법을 제공합니다. 이는 경미한~극심한 시력 문제를 가진 사람들이 직면하는 문제를 이해하는 데 도움이 될 수 있습니다.

\n

🔗 색상이 정보의 유일한 단서가 되어서는 안 된다

\n

앞서 언급했듯이, 많은 남성들이 시력 결함을 가지고 있습니다. 유형도 다양합니다. 가장 흔한 유형 중 하나인 중색 이상(Deuteranomaly)은 빨강과 녹색을 구분하기 어렵게 만듭니다. 색상 시력 결함이 있는 사람은 인터페이스를 사용할 수 없게 될 수 있으므로 색상만을 시각적 단서로 사용하는 것을 피해야 합니다.

\n

이전 예시에서 보여준 폼의 입력 필드에 성공 및 오류 상태를 나타내는 테두리를 추가했습니다. 다음 스크린샷은 색상만으로 사용자에게 충분한 피드백을 주지 못한다는 것을 보여줍니다. 테두리 색상이 전혀 보이지 않거나 잘못 보이는 경우가 있습니다.

\n

\"\"

\n

고대비 모드에서 색상만으로 폼의 성공과 실패를 구별하는 것은 효과적이지 않습니다.

\n

간단한 아이콘을 추가하면 접근성과 사용자 경험을 개선하는 데 도움이 될 수 있습니다.

\n

링크도 비슷한 예시 중 하나입니다. 링크는 색상만으로 일반 텍스트와 구별되어서는 안 됩니다. 링크에 밑줄을 유지하는 것이 좋습니다.

\n

🔗 순서에 신경 쓰기

\n

아이템들을 배치 순서를 바꾸는 방법은 많이 있습니다. 예를 들어, Flexbox에는 orderflex-direction이 있고, Grid에는 order, flex-auto-flow 및 명시적 배치가 있습니다. 이러한 속성들은 매우 유용하지만, 콘텐츠의 DOM 순서와 시각적 표현 사이의 연결이 끊어질 수 있습니다.

\n

다음 예시에서는 여러 그리드 속성을 사용하여 배치된 갤러리의 이미지를 볼 수 있습니다.

\n\n

처음에는 문제가 없어 보이지만, 키보드를 사용하여 이미지에서 이미지로 이동할 때 순서를 전혀 예측할 수 없다는 것을 알 수 있습니다. Tab 키를 눌렀을 때 다음에 어떤 이미지가 강조될지 알 수가 없습니다. 여기에 포커스 스타일이 없으면 최악의 상황이 될 수 있습니다.

\n\n

예측 불가능하거나 잘못된 순서는 키보드 사용자뿐만 아니라 스크린 리더 사용자에게도 문제가 됩니다. 스크린 리더는 DOM 순서대로 콘텐츠를 표시하므로 소프트웨어는 CSS 순서에 영향을 받지 않지만 사용자는 영향을 받습니다. 스크린 리더 사용자는 콘텐츠의 시각적 표현에 신경 쓰지 않는다고 생각할 수 있지만, 모든 스크린 리더의 사용자가 시각 장애가 있는 것은 아닙니다. 일부는 저시력이나 학습 장애가 있어 화면에 표시되는 내용을 보완하기 위해 스크린 리더를 사용합니다.

\n

이 순서 문제는 플렉스나 그리드 아이템뿐만 아니라 모든 종류의 위치 지정에도 적용됩니다. 스타일 없이도 의미가 있는 방식으로 콘텐츠를 정렬하는 것이 중요하며, 디자인에서의 순서와 일치하는지 확인해야 합니다. 일치하지 않으면 디자인을 다시 생각해야 할 수 있습니다. CSS에서 올바르게 위치시키지 못한다고 해서 마크업에서 요소들을 임의로 재배열하지 마세요.

\n

Rob Dodson’s의 콘텐츠 재배열이 접근성에 영향을 미치는가?와 Adrian Roselli의 코드의 순서가 중요하다 글을 참고해 더 자세한 내용을 알아보세요.

\n

🔗 중요한 것에 집중하기: focus

\n

저는 이미 키보드 탐색 기초와 포커스 가능한 요소들에 대해 접근성을 고려하여 자바스크립트 작성하기라는 글을 썼습니다. 이 주제가 처음이시라면, 그 글을 빠르게 읽고 오세요.

\n

키보드를 사용하여 웹사이트를 탐색할 수 있도록 하는 것은 중요합니다. 많은 사용자들이 웹 서핑을 할 때 키보드에 의존합니다. 그들 중에는 운동 장애가 있는 사람들, 시각 장애가 있는 사람들, 손이 없거나 어떤 이유로든 마우스나 트랙패드를 사용할 수 없는 사람들이 있습니다.

\n

CSS를 사용해서 포커스 가능한 요소들에 스타일을 적용할 수 있는 몇 가지 방법이 있습니다.

\n

포커스된 아이템 선택하기**

\n

:focus 의사 클래스를 사용하여 포커스 가능한 아이템들이 포커스 되었을때 스타일을 적용할 수 있습니다.

\n
a:focus {\n  background-color: #000000;\n  color: #FFFFFF;\n}
\n

기본 포커스 스타일은 브라우저마다 일관성이 없으며 보기 좋지 않은 경우가 많으며, 때로는 디자인과 잘 어울리지 않습니다. 사용자 경험을 개선하고 디자인에 맞는 맞춤 포커스 스타일을 제공하는 것이 좋습니다.

\n

하지만 무엇을 하든, 대체 스타일을 제공하지 않고 기본 윤곽선(점선 윤곽, 파란색 또는 주황색 반지)만 제거하지 마세요. 주로 키보드로 탐색하는 사용자들은 포커스 위치를 알 수 없다면 사이트를 사용할 수 없게 됩니다.

\n

\"\"

\n

대안 없이 기본 포커스 스타일을 제거하지 마세요 (출처: outlinenone.com)

\n

이것은 단순한 팁이 아니라 레벨 AA 기준 기준입니다.

\n

키보드와 마우스 사용자 구분하기

\n

이미 언급했듯이, 디자이너들이 좌절하는 것 중 하나는 브라우저 간 포커스 스타일의 일관성이 부족하다는 것입니다. 또한 마우스를 사용할 때도 일부 포커스 가능한 요소들에서 포커스 스타일이 보이는 점도 문제입니다. 때로는 마우스 사용자에게는 포커스 스타일을 보여줄 필요가 없으며, 심지어 방해가 되거나 미학적으로 불쾌할 수 있습니다.

\n

\"\"

\n

컨텐츠 영역이 클릭되었을 때 크롬에서 파란색 윤곽선이 보이는 맞춤 탭 컴포넌트 (출처: frend.co)

\n

웹 페이지의 특정 요소에 포커스가 가 있을 때 outline 속성을 제거해서는 안됩니다. 왜냐하면 컴포넌트가 더 이상 키보드 사용자에게 접근 불가능하기 때문입니다. 우리에게 필요한 것은 키보드와 마우스 사용을 구별하는 방법입니다. 이것은 CSS Level 4 선택자 명세의 일부인 :focus-ring 가상 클래스를 사용하여 가능 합니다. ”:focus-ring 가상 클래스는 요소가 :focus 가상 클래스와 일치하고, 사용자 에이전트(브라우저)가 휴리스틱(규칙이나 패턴)을 통해 요소에 특별히 표시되어야 함을 결정할 때 (일반적으로 ‘focus-ring’을 통해) 적용됩니다.” (출처: CSS 선택자 레벨 4 초안)

\n
/* 기본 윤곽선 제거 */\n:focus {\n  outline: none;\n}\n\n/* 윤곽선이 보여야 할 때만 추가 */\n:focus-ring {\n  outline: 2px solid blue;\n}
\n

아쉽게도 현재 어떤 브라우저도 :focus-ring의 표준 구현을 지원하지 않습니다. (Firefox는 -moz-focus-ring을 지원함), 하지만 적절할 때 .focus-ring 클래스를 추가하는 경량 폴리필이 있습니다.

\n
/* 자바스크립트가 활성화되어 작동하고, .focus-ring 클래스가 없는 \n모든 포커스 가능한 요소를 선택하여 윤곽선을 제거합니다 */\n.js-focus-ring :focus:not(.focus-ring) {\n    outline-width: 0;\n}
\n

자세한 내용은 Rob Dodson의 a11ycasts 에피소드, Focus Ring!을 시청하세요.

\n

포커스된 자식이 있는 요소에 대한 스타일링

\n

:focus-within는 상대적으로 새로운 가상 클래스로, 이미 대부분의 주요 브라우저에서 지원되고 있습니다. 이 클래스를 사용하면 현재 포커스된 자식 요소를 가진 요소를 선택할 수 있습니다.

\n

\"\"

\n

자식 항목 중 하나가 포커스되면 그림자가 표시되는 예시입니다.

\n
form:focus-within {\n  box-shadow: 0 0 4px 6px rgba(80,88,156,0.2);\n}
\n

이 기능을 CodePen에서 확인할 수 있습니다.

\n

포커스에 대한 자세한 내용은 YouTube에서 포커스란 무엇인가요? 영상을 참조하세요.

\n

🔗 그리드와 평평한 문서 구조

\n

새로운 사이트를 만들 때, 우리는 보통 HTML 작성부터 시작합니다. 적절한 마크업을 선택하고 요소들을 논리적 순서에 맞게 배치합니다. 문서가 올바르게 작성되어 있고 잘 구조화되어 있으며 순서가 의미 있게 되면 CSS를 추가합니다. CSS Grid Layout이 나오기 전에는, 특히 DOM 순서와 디자인 순서가 일치하지 않은 경우에 레이아웃을 만드는 것이 매우 까다로웠습니다. float, position 그리고 때때로 Flexbox조차도 어떤 상황에서는 충분히 유연하지 않았고, 우리는 DOM 순서를 변경하고 싶은 유혹에 빠지곤 했습니다. Grid의 명시적 배치와 해당 영역 덕분에 아이템을 위치시키는 데 필요한 모든 유연성을 갖게 되었습니다. 이것은 훌륭하지만, Grid는 문서 구조를 해칠 수 있는 새로운 유혹을 도입합니다.

\n

다음과 같은 디자인을 가지고 있고, 그 아이템들에 h2ul을 사용한다고 가정해 봅시다. 왜냐하면 그것이 당신에게 가장 의미가 있기 때문입니다.

\n

\"\"

\n

제목과 목록이 있는 레이아웃

\n
<div class="wrapper">\n  <h2>Heading</h2>\n  <ul>\n    <li><a href="#">Element 1</a></li>\n    <li><a href="#">Element 2</a></li>\n    <li><a href="#">Element 3</a></li>\n    <li><a href="#">Element 4</a></li>\n    <li><a href="#">Element 5</a></li>\n    <li><a href="#">Element 6</a></li>\n  </ul>\n</div>
\n

이러한 요소들을 열에 넣고 <h2>를 위치시키는 것은 꽤 쉽습니다… 또는 적어도 그렇게 보입니다.

\n
.wrapper {\n  display: grid;\n  grid-template-columns: 120px repeat(2, 1fr);\n  grid-gap: 20px;\n}h2 {\n  grid-column: 2 / -1;\n}
\n

\"\"

\n

제목과 목록이 있는 레이아웃. 그리드 컨테이너의 직접적인 자식만 그리드에 배치됩니다.

\n

하지만 예상과는 달리 보입니다. 문제는 그리드 컨테이너의 직접적인 자식만이 그리드에 배치된다는 것인데, 이 경우에는 <h2><ul>이 해당됩니다. 하지만 여러분은 <li>들을 그리드 아이템으로 만들고 싶습니다. 이 문제에 대한 최악의 해결책은 구조를 단순화시켜 <ul>을 제거하고 <li><div>로 변환하여 그리드 컨테이너의 직접적인 자식으로 만드는 것입니다.

\n

가장 좋은 해결책은 <ul>display 속성을 subgrid로 설정하는 것이지만, 불행히도 subgrid는 명세의 레벨 1에 포함되지 않았고 우리는 그것이 출시될 때까지 더 기다려야 합니다.

\n

<ul>display: contents를 사용할 수 있지만, 현재 Firefox만이 이를 지원합니다. display: contents는 요소의 자식들이 마치 요소의 부모의 직접적인 자식인 것처럼 보이게 만듭니다.

\n

결국, **<ul>**에 다른 그리드를 정의해야 합니다. 이것은 이상적이지 않지만, 문서의 구조를 단순화시키고 의미를 해치는 것보다는 낫습니다. 이것은 매우 기본적인 예제이며 목록이 전체 그리드를 차지하기 때문에 부모 그리드에서 일부 값을 상속받을 수 있습니다.

\n
ul {\n  /* 전체 그리드를 차지함 */\n  grid-column: 1 / -1;  /* 다른 그리드를 생성하고 부모 그리드에서 값을 상속받음 */\n  display: inherit;\n  grid-template-columns: inherit;\n  grid-gap: inherit;  /* display: contents를 이해하는 브라우저를 위해 display 덮어쓰기 */\n  display: contents;\n}
\n

CodePen에서 두 가지 솔루션을 볼 수 있습니다.

\n

결론

\n

이 글에서 꽤 많은 내용을 다루고 있지만 CSS와 접근성에 대해 알아야 할 모든 것을 다루지는 않습니다. 그러나 이것은 단순한 출발점 그 이상입니다. DOM 및 포커스 순서를 올바르게 설정하고 고대비에 신경 쓰고 일반적으로 접근성을 고려하여 디자인 하는 것만으로도 이미 훌륭한 작업을 하고 있는 것입니다. 새로운 페이지나 사이트를 만들 때마다 접근성을 약간만 더 고려한다면 웹을 더 나은 곳으로 만들 수 있습니다.

\n
\n

제약사항을 고려하여 디자인하는 것은 단순히 잘 디자인하는 것이다.

\n
\n

Aaron Gustafson

\n

이 글을 즐겁게 읽으셨고 새로운 것을 배우셨기를 바랍니다. 질문이나 피드백이 있으시면 댓글을 남겨주시거나 트위터를 통해 연락해 주시기 바랍니다.

\n

이 글을 작성하는 데 도움을 주신 멘토 Aaron Gustafson에게 감사드립니다.

\n

더 많은 접근성 팁

\n

이 글은 네 부분 시리즈 중 세 번째입니다. 마지막 글은 준비 중이며 곧 공개될 예정입니다.

\n
    \n
  1. 접근성을 고려한 HTML 작성하기
  2. \n
  3. 접근성을 고려한 JavaScript 작성하기
  4. \n
  5. 접근성을 고려한 CSS 작성하기
  6. \n
  7. 다음 글: 접근성을 고려한 디자인 및 개발 방법 배우기
  8. \n
\n

독자 여러분의 독서와 이 글을 좋아하시고 공유해 주시면 감사하겠습니다.

\n

제가 작성한 다른 글도 확인해보실 수 있습니다.

\n

Progressively Enhancing CSS Layout: From Floats To Flexbox To Grid

\n

The Difference Between Explicit and Implicit Grids

\n

추가적인 읽을 거리와 자료들

\n

가독성 있는 텍스트 작성법

\n\n

가상 요소에 콘텐츠 신중하게 사용하기

\n\n

화면만이 유일한 매체가 아니다

\n\n

완전히 지원되지 않는 속성 값에 대한 대안

\n\n

콘텐츠를 숨기는 여러 가지 방법

\n\n

나쁜 대비는 신뢰할 수 없다

\n\n

색상이 정보의 유일한 단서가 되어서는 안 된다

\n\n

순서에 신경 쓰기

\n\n

중요한 것에 집중하기: focus

\n\n

그리드와 평평한 문서 구조

\n\n","frontmatter":{"date":"February 18, 2024","title":"(번역) 접근성을 고려하여 CSS 작성하기","categories":"translate a11y","author":"soobing"},"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}},"prev":{"id":"f5dbfa6e-986a-5edb-9bc0-2f30132ce702","html":"
\n

원글: https://aralroca.com/blog/html-node-streaming

\n
\n

\"썸네일\"

\n

최근 몇 년간 브라우저는 HTML과 자바스크립트를 스트리밍을 지원하기 시작했습니다. 이 글에서는 이에 대한 장점과 브라우저가 자동으로 수행하지 않는 다른 작업들을 통해 스트리밍의 이점을 최대한 활용할 수 있는 방법에 대해 이야기할 것입니다.

\n

스트리밍 HTML

\n

초기 로드하는 동안에는 브라우저가 자동으로 처리하기 때문에 크게 신경 쓸 필요가 없습니다. 스트리밍 하는 동안 HTML 청크를 받으면, 브라우저는 그 콘텐츠를 출력합니다.

\n

서버에서 스트리밍을 활성화하려면 헤더를 조정해야 합니다. 아래는 예시입니다.

\n
{\n  "transfer-encoding": "chunked",\n  "vary": "Accept-Encoding",\n  "content-type": "text/html; charset=utf-8"\n}
\n

그리고 응답에서는 ReadableStream을 사용합니다. 이것은 Bun을 사용한 예시입니다.

\n
const encoder = new TextEncoder()\n\n// ...\n\nreturn new Response(\n  new ReadableStream({\n    start(controller) {\n      controller.enqueue(encoder.encode('<html lang="en">'))\n      controller.enqueue(encoder.encode('<head />'))\n      controller.enqueue(encoder.encode('<body>'))\n      controller.enqueue(encoder.encode('<div class="foo">Bar</div>'))\n      controller.enqueue(encoder.encode('</body>'))\n      controller.enqueue(encoder.encode('</html>'))\n      controller.close()\n    },\n  })\n)\n
\n

enqueue 안에 있는 각 문자열은 브라우저가 받게 될 청크입니다.

\n

스트리밍 중 HTML 콘텐츠 변경

\n

성능상의 이점이 많기 때문에 많이 사용되는 방법 중 하나는 스트리밍 되는 동안 HTML 콘텐츠를 변경하는 것입니다. 대표적인 예가 React Suspense입니다. 이 아이디어는 HTML의 나머지 부분을 로드하는 동안 빈 콘텐츠(플레이스홀더, 스켈레톤 또는 스피너)를 보여주고 그동안 누락된 콘텐츠를 로드하는 것입니다. 서버에 누락된 콘텐츠가 있으면 스트리밍 시간에 이를 변경합니다!

\n

아마 당신이 처음 이 내용을 듣는다면 이런 궁금증이 생길겁니다. 브라우저에 의해 이미 전송되어 처리된 HTML의 일부를 어떻게 수정할 수 있다는 것일까요 🤔?

\n

음, 브라우저는 스트리밍하는 동안 작은 JS 스크립트를 실행할 수 있을 만큼 충분히 똑똑합니다. 하지만, module(모듈) 타입이 아닌 스크립트여야 합니다. 왜냐하면 모듈 타입은 항상 모든 HTML이 로드될 때까지 실행 되지 않고 기다리기 때문입니다. 그래서 이번 경우 모듈 타입에는 관심이 없습니다.

\n

다음은 시각적으로 이해하기 쉽게 만든 예시 입니다.(보통 이것보다 복잡합니다.)

\n
return new Response(\n  new ReadableStream({\n    async start(controller) {\n      const suspensePromises = []\n\n      controller.enqueue(encoder.encode('<html lang="en">'))\n      controller.enqueue(encoder.encode('<head>'))\n      // "unsuspense"를 허용하는 코드 로드\n      controller.enqueue(\n        enconder.encode('<script src="unsuspense.js"></script>')\n      )\n      controller.enqueue(encoder.encode('</head>'))\n      controller.enqueue(encoder.encode('<body>'))\n\n      // 플레이스홀더 추가 (suspense)\n      controller.enqueue(\n        encoder.encode('<div id="suspensed:1">Loading...</div>')\n      )\n\n      // 콘텐츠 로드 - "await" 없음 (중요)\n      suspensePromises.push(\n        computeExpensiveChunk().then((content) => {\n          // 실제 콘텐츠 큐 인입\n          controller.enqueue(\n            encoder.encode(\n              `<template id="suspensed-content:1">${content}</template>`\n            )\n          )\n          // suspensed 콘텐츠를 실제 콘텐츠로 대체할 스크립트를 enqueue\n          controller.enqueue(encoder.encode(`<script>unsuspense('1')</script>`))\n        })\n      )\n\n      controller.enqueue(encoder.encode('<div class="foo">Bar</div>'))\n      controller.enqueue(encoder.encode('</body>'))\n      controller.enqueue(encoder.encode('</html>'))\n\n      // 스트림을 닫기 전에 suspend된 모든 콘텐츠를 기다림\n      await Promise.all(suspensePromises)\n\n      controller.close()\n    },\n  })\n)
\n

unsuspense.js 파일은 window.unsuspense를 노출하여 스트리밍 중에 실행될 수 있게 하고, 이를 통해 suspensed:1의 콘텐츠를 suspensed-content:1 템플릿의 콘텐츠로 교체 합니다. 이 경우, 사용자는 Loading... 텍스트와 Bar텍스트가 있는 div를 볼 수 있습니다. 콘텐츠가 처리되면, Loading... 콘텐츠는 실제 콘텐츠로 변경됩니다.

\n

이점을 생각해보면, 모든 것이 단일 요청으로 이루어지고 사용자는 추가 요청을 하지 않아도 HTML과 그 변경사항을 즉시 확인 할 수 있습니다. 지난 몇 년 동안 이러한 요청은 클라이언트에서 만들어졌습니다. 예를 들어 React에서는 useEffect를 사용했고, 모든 HTML이 로드될 때까지 실행되지 않았으며, 서버에 다른 추가 요청을 하여 개발자들의 작업을 복잡하게 만들었습니다.

\n

정리해보면, 스트리밍 중에 HTML 콘텐츠를 수정할 수 있게 되어 async 컴포넌트를 사용할 수 있고, Suspense와 함께 직접 fetch를 사용하거나 데이터베이스 요청을 할 수 있게 되었습니다.

\n

런타임 중 HTML 스트리밍

\n

우리는 HTML 스트리밍의 실용성에 대해 HTML의 초기 로딩에 초점을 맞춰 이야기했습니다. 그러나, 초기 로딩 외에도 HTML을 스트리밍하고자 하는 다른 시나리오가 있을까요?

\n

네, 두 가지 경우가 있습니다.

\n\n

내비게이션 (뷰 트랜지션 API)

\n

2023년부터 크롬이 뷰 트랜지션 API를 발표했으며, 사파리도 지원할 것으로 보입니다.

\n

뷰 트랜지션 API는 싱글 페이지 애플리케이션(SPA)을 모방하여, 다양한 DOM 상태 간의 애니메이션 전환을 쉽게 생성할 수 있는 메커니즘을 제공하면서 동시에 DOM 콘텐츠를 한 번에 업데이트합니다.

\n

내비게이션 중에는 보통 모든 HTML 콘텐츠를 다른 콘텐츠로 바꾸고 싶어 합니다. 하지만, 우리는 대기할 필요 없이 스트리밍의 이점을 최대한 활용해야 합니다.

\n
window.navigation.addEventListener('navigate', navigate)\n\nfunction navigate(event) {\n  const url = new URL(event.destination.url)\n  const decoder = new TextDecoder()\n\n  // 보안을 위해 동일 출처 내비게이션만 가로챕니다\n  if (location.origin !== url.origin) return\n\n  event.intercept({\n    async handler() {\n      const res = await fetch(url.pathname)\n      // 새로운 "샌드박스" HTML 문서 생성\n      const doc = document.implementation.createHTMLDocument()\n      const stream = res.body.getReader()\n\n      // 부드러운 스크롤링과 함께 새 문서로 전환\n      await document.startViewTransition(() => {\n        // 현재 문서의 DOM과 샌드박스의 DOM을 연결\n        document.documentElement.replaceWith(doc.documentElement)\n        document.documentElement.scrollTop = 0\n      }).ready\n\n      // 응답 body를 청크로 처리\n      while (true) {\n        const { done, value } = await stream.read()\n\n        if (done) break // 스트림이 끝나면 루프 종료\n\n        // 트랜지션 중에 샌드박스 문서에 디코드된 콘텐츠 주입\n        // 샌드박스는 문자열을 처리하고 파싱한 다음, 트랜지션 중에\n        // 실제 DOM으로 옮긴다.\n        await document.startViewTransition(() =>\n          doc.write(decoder.decode(value))\n        ).ready\n      }\n    },\n  })\n}\n
\n

이 예시에서는 document.implementation.createHTMLDocumentdoc.write와 함께 사용되어 스트림을 처리함으로써 각 청크를 다른 트랜지션으로 만듭니다. doc.write는 경고를 일으키지 않아야 하는데, 이 컨텍스트에서는 잘 사용되었습니다. 이 트릭에 대한 더 자세한 내용은 크롬에서 제공된 영상을 살펴보세요.

\n

위의 예시는 오직 모든 HTML 콘텐츠를 다른 콘텐츠로 대체하기 위해서만 사용되지만, 스트리밍 중에 HTML 노드들을 더 효과적으로 제어하고 싶다면 어떨까요? 아마도 이 스트리밍 중에 특정 노드들을 필터링할 수도 있을 것입니다.

\n

이를 가능하게 하기 위해, 스트림 리더를 HTML 노드 생성기로 쉽게 변환할 수 있게 하는 parse-html-stream 라이브러리를 사용할 수 있습니다. 이 경우, 다음과 같이 사용할 수 있습니다.

\n
import parseHTMLStream from 'parse-html-stream' // 임포트\n\nwindow.navigation.addEventListener('navigate', navigate)\n\nfunction navigate(event) {\n  const url = new URL(event.destination.url)\n  const decoder = new TextDecoder()\n\n  if (location.origin !== url.origin) return\n\n  event.intercept({\n    async handler() {\n      const res = await fetch(url.pathname)\n      const doc = document.implementation.createHTMLDocument()\n      const stream = res.body.getReader()\n\n      await document.startViewTransition(() => {\n        document.documentElement.replaceWith(doc.documentElement)\n        document.documentElement.scrollTop = 0\n      }).ready\n\n      // 사용하기\n      for await (const node of parseHTMLStream(reader)) {\n        // 스트리밍 중에 각 HTML 노드를 완전히 통제 가능\n        console.log(node.nodeName)\n      }\n    },\n  })\n}\n
\n

그러나 이제 메인 document의 올바른 위치에 노드를 추가하는 것이 훨씬 더 복잡해지지 않았나요? 그렇다면 이 모든 것이 의미가 있을까요?

\n

정확하게 이해하기 위해 조금만 더 알아보죠.

\n

RPCs (예: 서버 액션)

\n

원격 프로시저 호출(RPC)은 개발자가 엔드포인트를 구현할 필요가 없고 브라우저-서버와 서버-브라우저 간의 모든 통신 로직이 RPC에 의해 처리되도록 사용됩니다. RPC의 예로는 스트리밍 도중 DOM 업데이트를 트리거할 수 있는 React 서버 액션이 있습니다. 하지만, 여기서 React는 하이퍼미디어(HTML)를 전송하는 대신 가상 DOM을 사용하고 실제 DOM과 함께 diff(비교) 알고리즘을 사용합니다.

\n

이 글에서는 RPC 구현 방법에 대한 기술적인 세부 사항을 설명하지 않겠지만, React와 다른 컨텍스트에서 서버 액션이 어떻게 작동할지 이해하기 위해, onClick과 같은 이벤트가 서버 컴포넌트에서 작동하는 예시를 보여주는 이 동영상을 시청 하시길 바랍니다. 즉, 사용자가 브라우저에서 버튼을 클릭하면 onClick 함수가 서버에서 실행됩니다.

\n\n

이 트윗의 영상은 제가 진행 중인 실험적인 프레임워크에 대한 것으로, 몇 달 내에 공개하여 더 자세한 정보를 제공할 수 있기를 바랍니다.

\n

서버에서 DB나 원하는 모든 것을 수정할 수 있고 브라우저에 반영된 HTML 변경 사항을 볼 수 있다는 것이 매력적입니다.

\n\n

이를 가능하게 하기 위해, RPC 클라이언트 코드(200B)에서 액션에 대한 요청을 하고, 첫 번째 요청인 경우, 응답을 처리하기 위한 지연 로딩 클라이언트 코드(1kb)를 다운로드하기 위해 다시 요청을 합니다. 이 마지막 코드는 DOM 비교 알고리즘을 사용하여 변경된 부분에 대해서만 HTML을 처리하고 DOM을 업데이트하는 역할을 담당합니다.

\n

이것의 이점을 이해하기 위해 클라이언트에서 거의 JS 없이 SPA를 구축할 수 있다고 생각해보세요. 단지 RPC만 있으면 됩니다. 다만 서버 액션은 서버를 포함하는 상호작용에 대해 의미가 있습니다. 순수하게 클라이언트 상호작용이나 Web API가 필요한 경우에는 웹 컴포넌트를 사용할 수 있습니다.

\n

전체 DOM이 아닌 DOM의 수정된 부분만 업데이트하는 또 다른 이점은 웹 컴포넌트와의 탁월한 통합입니다. 변경사항에 반응하기 위해 signal을 활용하는 웹 컴포넌트는 동기화를 잃지 않고 내부 상태를 유지할 수 있습니다.

\n

DOM 비교 알고리즘

\n

React는 문서 객체 모델 (DOM)을 업데이트하기 위해 비교 알고리즘을 사용하지만 가상 DOM을 사용하므로, 하이퍼미디어(HTML) 대신 가상 DOM을 직접 전송합니다. 이는 클라이언트 컴포넌트는 DOM에 반응하지 않기 때문에 더 효과적으로 제어하기 위함입니다. 반면, 우리의 클라이언트 컴포넌트가 시그널을 사용하는 웹 컴포넌트인 경우, 프로퍼티들은 반응형이며 새로운 어트리뷰트를 추가하여 엘리먼트가 업데이트되면, 웹 컴포넌트는 내부 상태를 잃지 않고 변경 사항에 반응합니다. 이를 통해 HTML을 직접 전송하고 DOM과 직접 작업할 수 있습니다.

\n

현재 여러 오픈소스 DOM 비교 알고리즘이 있으며, 아래 몇 가지 예시가 있습니다.

\n\n

대부분의 구현은 너비 우선 탐색(BFS)을 사용하여 DOM 트리를 탐색하고 업데이트합니다.

\n

\"\"

\n

예시: 너비 우선 탐색(BFS) 애니메이션

\n

그러나 HTML 스트리밍 중에는 노드가 깊이 우선 탐색(DFS) 순서로 도착합니다.

\n

\"\"

\n

예시: 깊이 우선 탐색(DFS) 애니메이션

\n

서버가 가능한 한 빨리 HTML을 전송을 시작할 수 있고 브라우저가 전체 응답이 도착할 때까지 기다리지 않는다면, 도착하는 대로 HTML 코드를 조각으로 처리할 수 있습니다.

\n

\"\"

\n

서버에서 제공하는 HTML 코드의 파싱 및 렌더링

\n

마지막 이미지는 브라우저가 HTML 스트리밍을 처리하는 방법이지만, 스트리밍을 위한 DOM 비교 알고리즘을 지원하려면 동일한 작업을 수행해야 합니다.

\n

페이지 변경사항을 사용자에게 최대한 빨리 제공하기 위해 렌더링 작업이 점진적으로 진행됩니다. 이 접근 방식은 페이지의 상호작용 이후 다음 페인트까지(INP) 점수를 더 높게 만듭니다.

\n

청크가 DFS 순서로 도착하더라도, 브라우저는 엘리먼트를 정의하는 모든 정보를 받기 전까지는 렌더링할 수 없습니다.

\n

내비게이션에 대해 이야기했을 때를 기억해보면, 우리는 다음과 같은 예를 들었습니다.

\n
for await (const node of parseHTMLStream(reader)) {\n  // 스트리밍 중에 각 HTML 노드를 완전히 제어할 수 있습니다.\n  console.log(node.nodeName)\n}
\n

이 시점에서, 노드는 항상 DFS 순서로 도착합니다. 하지만 이것만으로는 충분하지 않습니다. 왜냐하면 우리는 DOM 비교 알고리즘 내부의 트리를 살펴봐야 하기 때문입니다. 즉, firstChild, nextSibling, parentNode 등을 방문하여 실제 DOM 노드와 비교해야 합니다.

\n

이를 위해 제가 최근에 오픈소스로 공개한 parse-html-stream 라이브러리는 스트리밍 중에 노드 트리를 순회하는 것을 지원하여 점진적인 렌더링을 수행할 수 있습니다. 노드 트리를 순회하는 동안 라이브러리가 HTML 청크를 도착하는 순서대로 파싱하고, 파싱된 노드가 있을 때 렌더링 변경을 할 수 있게 해줍니다.

\n
import htmlStreamWalker from 'parse-html-stream/walker'\n\n// ...\n\nconst reader = res.body.getReader()\nconst walker = await htmlStreamWalker(reader)\n\n// 루트 노드\nconst rootNode = walker.rootNode\n\n// 스트림 청크를 고려하여 firstChild를 제공\nconst child = await walker.firstChild(rootNode)\n\n// 스트림 청크를 고려하여 nextSibling을 제공\nconst brother = await walker.nextSibling(rootNode)\n\n// 모든 HTML 노드에서 수행 가능\nconst childOfBrother = await walker.firstChild(brother)\n
\n

이 경우 rootNode, child, brother, childOfBrother는 노드입니다. 그리고 우리는 모든 노드 속성에 접근할 수 있습니다. 그러나 스트리밍 청크가 아직 도착하지 않았기 때문에 다음 두 속성이 참이 아닐 수 있음에 유의하세요.

\n\n

이러한 경우를 위해 parse-html-stream walker는 항상 작동하도록 그들을 대체하는 두 메서드를 제공합니다.

\n\n

이러한 함수의 실행 후에도 초기 노드들은 컨텍스트를 잃지 않고 사용될 수 있습니다.

\n

결론

\n

이 글에서는 초기 렌더링을 넘어 HTML 스트리밍의 이점과 실용적인 응용에 대해 이야기했습니다.

\n

우리는 RPC, 뷰 트랜지션 API, DOM 비교 알고리즘 같은 더 기술적인 개념들에 대해 언급했지만, 각각의 주제에 대해 자세히 들어가기보다는 각각에서 HTML 스트리밍을 어떻게 사용하는지에 대해 이야기했습니다. 이 글에서 깊이 다루지 않은 주제에 대해 더 알고 싶다면, 댓글로 달아주시면 주제에 초점을 맞춘 다른 글을 작성하는 데 참고하겠습니다.

\n

또한, parse-html-stream에 대해서도 이야기했는데, 이는 제가 최근에 오픈소스로 공개한 작은 라이브러리로 누구나 사용할 수 있습니다.

\n

마지막으로, 웹은 하이퍼미디어(HTML)를 전송하기 위해 발명되었습니다. 지난 몇 년 동안 JSON과 많은 클라이언트 코드를 사용했지만, 개발을 더 쉽게 하고 매우 적은 자바스크립트 코드로 웹사이트를 더 가볍게 만들 수 있는 하이퍼미디어 주도(Hypermedia-Driven) 응용 프로그램이 더 많이 나오기를 바랍니다. 따라서, 저는 초기 로드를 넘어 HTML 스트리밍의 미래가 매우 밝다고 생각합니다.

\n

참조

\n\n","frontmatter":{"date":"March 21, 2024","title":"(번역) 스트리밍 HTML과 DOM 비교 알고리즘","categories":"translate react","author":"soobing"},"fields":{"slug":"/react/html-node-streaming/"}},"site":{"siteMetadata":{"siteUrl":"https://soobing.github.io","comments":{"utterances":{"repo":"soobing/soobing.github.io"}}}}},"pageContext":{"slug":"/browser/browser-coordinate-size-api/","nextSlug":"/a11y/writing-css-with-accessibility-in-mind/","prevSlug":"/react/html-node-streaming/"}}, + "result": {"data":{"cur":{"id":"e5b56e8b-108e-5455-8e4a-ebbfe2c18f09","html":"

프론트 개발을 하다보면 웹 페이지의 다양한 요소들을 직접 제어해야 하는 경우가 있습니다. 이 글에서는 CSSOM의 네 가지 주요 좌표 체계인 Offset, Page, Viewport, Screen에 대해 자세히 알아보고, 위치와 크기 관련 API는 어떤것이 있는지 살펴볼 예정입니다. 이 글을 통해 웹 페이지 내에서 요소의 위치와 크기를 정밀하게 제어하는 데 도움이 되길 바랍니다.

\n

표준 CSSOM 좌표 체계

\n

보통 클라이언트 개발에서 좌표의 원점은 왼쪽 하단이 아닌, 왼쪽 상단 입니다.

\n

CSS 객체 모델에서는 Offset, Page, Viewport, Screen 네 가지 표준 좌표 체계가 있습니다.

\n

\n \n \n

\n

출처: MDN Coordinate_systems

\n

Offset

\n\n

Page

\n\n

Viewport

\n\n

Screen

\n\n

참고: 상대적 위치 vs 절대적 위치

\n\n

브라우저 제공 API

\n
\n

💻 window 제공 API, 🌱 Element 제공 API

\n
\n

1. 브라우저 창 크기와 뷰포트

\n\n

2. 요소의 크기와 경계

\n\n

3. 스크롤 관련 정보

\n\n

4. 마우스 위치

\n\n

5. 스타일 정보

\n\n","excerpt":"프론트 개발을 하다보면 웹 페이지의 다양한 요소들을 직접 제어해야 하는 경우가 있습니다. 이 글에서는 CSSOM의 네 가지 주요 좌표 체계인 Offset, Page, Viewport, Screen에 대해 자세히 알아보고, 위치와 크기 관련 API는 어떤것이 있는지 살펴볼 예정입니다. 이 글을 통해 웹 페이지 내에서 요소의 위치와 크기를 정밀하게 제어하는 데 도움이 되길 바랍니다. 표준 CSSOM 좌표 체계 보통 클라이언트 개발에서 좌표의 원점은 왼쪽 하단이 아닌, 왼쪽 상단 입니다. CSS 객체 모델에서는 Offset, Page, Viewport, Screen 네 가지 표준 좌표 체계가 있습니다. 출처: MDN Coordinate_systems Offset 정의: 오프셋 좌표 체계는 특정 요소의 상대적 위치를 나타내는 데 사용됩니다. 이는 요소의 왼쪽 상단 모서리를 시작점(원점)으로 합니다. 특징: 이 좌표 체계는 요소의 부모 요소를 기준으로 한 상대적 위치를 제공합니다. 마…","frontmatter":{"date":"March 03, 2024","title":"브라우저 위치 및 크기 관련 API들","categories":"browser","author":"soobing"},"fields":{"slug":"/browser/browser-coordinate-size-api/"}},"next":{"id":"4cbf47cf-4a24-5ffd-88c3-bdede11e8a72","html":"

Docker 엔진 시작하기/종료하기

\n\n

컨테이너의 기본적인 사용 방법

\n\n

컨테이너 생성, 삭제, 실행, 정지

\n
docker run (옵션) 이미지 (인자)\ndocker stop 컨테이너_이름\ndocker rm 컨테이너_이름\ndocker ps -a
\n

예제

\n\n","frontmatter":{"date":"February 18, 2024","title":"docker에서 자주쓰는 명령어","categories":"infra","author":"soobing"},"fields":{"slug":"/infra/docker-command/"}},"prev":{"id":"f5dbfa6e-986a-5edb-9bc0-2f30132ce702","html":"
\n

원글: https://aralroca.com/blog/html-node-streaming

\n
\n

\"썸네일\"

\n

최근 몇 년간 브라우저는 HTML과 자바스크립트를 스트리밍을 지원하기 시작했습니다. 이 글에서는 이에 대한 장점과 브라우저가 자동으로 수행하지 않는 다른 작업들을 통해 스트리밍의 이점을 최대한 활용할 수 있는 방법에 대해 이야기할 것입니다.

\n

스트리밍 HTML

\n

초기 로드하는 동안에는 브라우저가 자동으로 처리하기 때문에 크게 신경 쓸 필요가 없습니다. 스트리밍 하는 동안 HTML 청크를 받으면, 브라우저는 그 콘텐츠를 출력합니다.

\n

서버에서 스트리밍을 활성화하려면 헤더를 조정해야 합니다. 아래는 예시입니다.

\n
{\n  "transfer-encoding": "chunked",\n  "vary": "Accept-Encoding",\n  "content-type": "text/html; charset=utf-8"\n}
\n

그리고 응답에서는 ReadableStream을 사용합니다. 이것은 Bun을 사용한 예시입니다.

\n
const encoder = new TextEncoder()\n\n// ...\n\nreturn new Response(\n  new ReadableStream({\n    start(controller) {\n      controller.enqueue(encoder.encode('<html lang="en">'))\n      controller.enqueue(encoder.encode('<head />'))\n      controller.enqueue(encoder.encode('<body>'))\n      controller.enqueue(encoder.encode('<div class="foo">Bar</div>'))\n      controller.enqueue(encoder.encode('</body>'))\n      controller.enqueue(encoder.encode('</html>'))\n      controller.close()\n    },\n  })\n)\n
\n

enqueue 안에 있는 각 문자열은 브라우저가 받게 될 청크입니다.

\n

스트리밍 중 HTML 콘텐츠 변경

\n

성능상의 이점이 많기 때문에 많이 사용되는 방법 중 하나는 스트리밍 되는 동안 HTML 콘텐츠를 변경하는 것입니다. 대표적인 예가 React Suspense입니다. 이 아이디어는 HTML의 나머지 부분을 로드하는 동안 빈 콘텐츠(플레이스홀더, 스켈레톤 또는 스피너)를 보여주고 그동안 누락된 콘텐츠를 로드하는 것입니다. 서버에 누락된 콘텐츠가 있으면 스트리밍 시간에 이를 변경합니다!

\n

아마 당신이 처음 이 내용을 듣는다면 이런 궁금증이 생길겁니다. 브라우저에 의해 이미 전송되어 처리된 HTML의 일부를 어떻게 수정할 수 있다는 것일까요 🤔?

\n

음, 브라우저는 스트리밍하는 동안 작은 JS 스크립트를 실행할 수 있을 만큼 충분히 똑똑합니다. 하지만, module(모듈) 타입이 아닌 스크립트여야 합니다. 왜냐하면 모듈 타입은 항상 모든 HTML이 로드될 때까지 실행 되지 않고 기다리기 때문입니다. 그래서 이번 경우 모듈 타입에는 관심이 없습니다.

\n

다음은 시각적으로 이해하기 쉽게 만든 예시 입니다.(보통 이것보다 복잡합니다.)

\n
return new Response(\n  new ReadableStream({\n    async start(controller) {\n      const suspensePromises = []\n\n      controller.enqueue(encoder.encode('<html lang="en">'))\n      controller.enqueue(encoder.encode('<head>'))\n      // "unsuspense"를 허용하는 코드 로드\n      controller.enqueue(\n        enconder.encode('<script src="unsuspense.js"></script>')\n      )\n      controller.enqueue(encoder.encode('</head>'))\n      controller.enqueue(encoder.encode('<body>'))\n\n      // 플레이스홀더 추가 (suspense)\n      controller.enqueue(\n        encoder.encode('<div id="suspensed:1">Loading...</div>')\n      )\n\n      // 콘텐츠 로드 - "await" 없음 (중요)\n      suspensePromises.push(\n        computeExpensiveChunk().then((content) => {\n          // 실제 콘텐츠 큐 인입\n          controller.enqueue(\n            encoder.encode(\n              `<template id="suspensed-content:1">${content}</template>`\n            )\n          )\n          // suspensed 콘텐츠를 실제 콘텐츠로 대체할 스크립트를 enqueue\n          controller.enqueue(encoder.encode(`<script>unsuspense('1')</script>`))\n        })\n      )\n\n      controller.enqueue(encoder.encode('<div class="foo">Bar</div>'))\n      controller.enqueue(encoder.encode('</body>'))\n      controller.enqueue(encoder.encode('</html>'))\n\n      // 스트림을 닫기 전에 suspend된 모든 콘텐츠를 기다림\n      await Promise.all(suspensePromises)\n\n      controller.close()\n    },\n  })\n)
\n

unsuspense.js 파일은 window.unsuspense를 노출하여 스트리밍 중에 실행될 수 있게 하고, 이를 통해 suspensed:1의 콘텐츠를 suspensed-content:1 템플릿의 콘텐츠로 교체 합니다. 이 경우, 사용자는 Loading... 텍스트와 Bar텍스트가 있는 div를 볼 수 있습니다. 콘텐츠가 처리되면, Loading... 콘텐츠는 실제 콘텐츠로 변경됩니다.

\n

이점을 생각해보면, 모든 것이 단일 요청으로 이루어지고 사용자는 추가 요청을 하지 않아도 HTML과 그 변경사항을 즉시 확인 할 수 있습니다. 지난 몇 년 동안 이러한 요청은 클라이언트에서 만들어졌습니다. 예를 들어 React에서는 useEffect를 사용했고, 모든 HTML이 로드될 때까지 실행되지 않았으며, 서버에 다른 추가 요청을 하여 개발자들의 작업을 복잡하게 만들었습니다.

\n

정리해보면, 스트리밍 중에 HTML 콘텐츠를 수정할 수 있게 되어 async 컴포넌트를 사용할 수 있고, Suspense와 함께 직접 fetch를 사용하거나 데이터베이스 요청을 할 수 있게 되었습니다.

\n

런타임 중 HTML 스트리밍

\n

우리는 HTML 스트리밍의 실용성에 대해 HTML의 초기 로딩에 초점을 맞춰 이야기했습니다. 그러나, 초기 로딩 외에도 HTML을 스트리밍하고자 하는 다른 시나리오가 있을까요?

\n

네, 두 가지 경우가 있습니다.

\n\n

내비게이션 (뷰 트랜지션 API)

\n

2023년부터 크롬이 뷰 트랜지션 API를 발표했으며, 사파리도 지원할 것으로 보입니다.

\n

뷰 트랜지션 API는 싱글 페이지 애플리케이션(SPA)을 모방하여, 다양한 DOM 상태 간의 애니메이션 전환을 쉽게 생성할 수 있는 메커니즘을 제공하면서 동시에 DOM 콘텐츠를 한 번에 업데이트합니다.

\n

내비게이션 중에는 보통 모든 HTML 콘텐츠를 다른 콘텐츠로 바꾸고 싶어 합니다. 하지만, 우리는 대기할 필요 없이 스트리밍의 이점을 최대한 활용해야 합니다.

\n
window.navigation.addEventListener('navigate', navigate)\n\nfunction navigate(event) {\n  const url = new URL(event.destination.url)\n  const decoder = new TextDecoder()\n\n  // 보안을 위해 동일 출처 내비게이션만 가로챕니다\n  if (location.origin !== url.origin) return\n\n  event.intercept({\n    async handler() {\n      const res = await fetch(url.pathname)\n      // 새로운 "샌드박스" HTML 문서 생성\n      const doc = document.implementation.createHTMLDocument()\n      const stream = res.body.getReader()\n\n      // 부드러운 스크롤링과 함께 새 문서로 전환\n      await document.startViewTransition(() => {\n        // 현재 문서의 DOM과 샌드박스의 DOM을 연결\n        document.documentElement.replaceWith(doc.documentElement)\n        document.documentElement.scrollTop = 0\n      }).ready\n\n      // 응답 body를 청크로 처리\n      while (true) {\n        const { done, value } = await stream.read()\n\n        if (done) break // 스트림이 끝나면 루프 종료\n\n        // 트랜지션 중에 샌드박스 문서에 디코드된 콘텐츠 주입\n        // 샌드박스는 문자열을 처리하고 파싱한 다음, 트랜지션 중에\n        // 실제 DOM으로 옮긴다.\n        await document.startViewTransition(() =>\n          doc.write(decoder.decode(value))\n        ).ready\n      }\n    },\n  })\n}\n
\n

이 예시에서는 document.implementation.createHTMLDocumentdoc.write와 함께 사용되어 스트림을 처리함으로써 각 청크를 다른 트랜지션으로 만듭니다. doc.write는 경고를 일으키지 않아야 하는데, 이 컨텍스트에서는 잘 사용되었습니다. 이 트릭에 대한 더 자세한 내용은 크롬에서 제공된 영상을 살펴보세요.

\n

위의 예시는 오직 모든 HTML 콘텐츠를 다른 콘텐츠로 대체하기 위해서만 사용되지만, 스트리밍 중에 HTML 노드들을 더 효과적으로 제어하고 싶다면 어떨까요? 아마도 이 스트리밍 중에 특정 노드들을 필터링할 수도 있을 것입니다.

\n

이를 가능하게 하기 위해, 스트림 리더를 HTML 노드 생성기로 쉽게 변환할 수 있게 하는 parse-html-stream 라이브러리를 사용할 수 있습니다. 이 경우, 다음과 같이 사용할 수 있습니다.

\n
import parseHTMLStream from 'parse-html-stream' // 임포트\n\nwindow.navigation.addEventListener('navigate', navigate)\n\nfunction navigate(event) {\n  const url = new URL(event.destination.url)\n  const decoder = new TextDecoder()\n\n  if (location.origin !== url.origin) return\n\n  event.intercept({\n    async handler() {\n      const res = await fetch(url.pathname)\n      const doc = document.implementation.createHTMLDocument()\n      const stream = res.body.getReader()\n\n      await document.startViewTransition(() => {\n        document.documentElement.replaceWith(doc.documentElement)\n        document.documentElement.scrollTop = 0\n      }).ready\n\n      // 사용하기\n      for await (const node of parseHTMLStream(reader)) {\n        // 스트리밍 중에 각 HTML 노드를 완전히 통제 가능\n        console.log(node.nodeName)\n      }\n    },\n  })\n}\n
\n

그러나 이제 메인 document의 올바른 위치에 노드를 추가하는 것이 훨씬 더 복잡해지지 않았나요? 그렇다면 이 모든 것이 의미가 있을까요?

\n

정확하게 이해하기 위해 조금만 더 알아보죠.

\n

RPCs (예: 서버 액션)

\n

원격 프로시저 호출(RPC)은 개발자가 엔드포인트를 구현할 필요가 없고 브라우저-서버와 서버-브라우저 간의 모든 통신 로직이 RPC에 의해 처리되도록 사용됩니다. RPC의 예로는 스트리밍 도중 DOM 업데이트를 트리거할 수 있는 React 서버 액션이 있습니다. 하지만, 여기서 React는 하이퍼미디어(HTML)를 전송하는 대신 가상 DOM을 사용하고 실제 DOM과 함께 diff(비교) 알고리즘을 사용합니다.

\n

이 글에서는 RPC 구현 방법에 대한 기술적인 세부 사항을 설명하지 않겠지만, React와 다른 컨텍스트에서 서버 액션이 어떻게 작동할지 이해하기 위해, onClick과 같은 이벤트가 서버 컴포넌트에서 작동하는 예시를 보여주는 이 동영상을 시청 하시길 바랍니다. 즉, 사용자가 브라우저에서 버튼을 클릭하면 onClick 함수가 서버에서 실행됩니다.

\n\n

이 트윗의 영상은 제가 진행 중인 실험적인 프레임워크에 대한 것으로, 몇 달 내에 공개하여 더 자세한 정보를 제공할 수 있기를 바랍니다.

\n

서버에서 DB나 원하는 모든 것을 수정할 수 있고 브라우저에 반영된 HTML 변경 사항을 볼 수 있다는 것이 매력적입니다.

\n\n

이를 가능하게 하기 위해, RPC 클라이언트 코드(200B)에서 액션에 대한 요청을 하고, 첫 번째 요청인 경우, 응답을 처리하기 위한 지연 로딩 클라이언트 코드(1kb)를 다운로드하기 위해 다시 요청을 합니다. 이 마지막 코드는 DOM 비교 알고리즘을 사용하여 변경된 부분에 대해서만 HTML을 처리하고 DOM을 업데이트하는 역할을 담당합니다.

\n

이것의 이점을 이해하기 위해 클라이언트에서 거의 JS 없이 SPA를 구축할 수 있다고 생각해보세요. 단지 RPC만 있으면 됩니다. 다만 서버 액션은 서버를 포함하는 상호작용에 대해 의미가 있습니다. 순수하게 클라이언트 상호작용이나 Web API가 필요한 경우에는 웹 컴포넌트를 사용할 수 있습니다.

\n

전체 DOM이 아닌 DOM의 수정된 부분만 업데이트하는 또 다른 이점은 웹 컴포넌트와의 탁월한 통합입니다. 변경사항에 반응하기 위해 signal을 활용하는 웹 컴포넌트는 동기화를 잃지 않고 내부 상태를 유지할 수 있습니다.

\n

DOM 비교 알고리즘

\n

React는 문서 객체 모델 (DOM)을 업데이트하기 위해 비교 알고리즘을 사용하지만 가상 DOM을 사용하므로, 하이퍼미디어(HTML) 대신 가상 DOM을 직접 전송합니다. 이는 클라이언트 컴포넌트는 DOM에 반응하지 않기 때문에 더 효과적으로 제어하기 위함입니다. 반면, 우리의 클라이언트 컴포넌트가 시그널을 사용하는 웹 컴포넌트인 경우, 프로퍼티들은 반응형이며 새로운 어트리뷰트를 추가하여 엘리먼트가 업데이트되면, 웹 컴포넌트는 내부 상태를 잃지 않고 변경 사항에 반응합니다. 이를 통해 HTML을 직접 전송하고 DOM과 직접 작업할 수 있습니다.

\n

현재 여러 오픈소스 DOM 비교 알고리즘이 있으며, 아래 몇 가지 예시가 있습니다.

\n\n

대부분의 구현은 너비 우선 탐색(BFS)을 사용하여 DOM 트리를 탐색하고 업데이트합니다.

\n

\"\"

\n

예시: 너비 우선 탐색(BFS) 애니메이션

\n

그러나 HTML 스트리밍 중에는 노드가 깊이 우선 탐색(DFS) 순서로 도착합니다.

\n

\"\"

\n

예시: 깊이 우선 탐색(DFS) 애니메이션

\n

서버가 가능한 한 빨리 HTML을 전송을 시작할 수 있고 브라우저가 전체 응답이 도착할 때까지 기다리지 않는다면, 도착하는 대로 HTML 코드를 조각으로 처리할 수 있습니다.

\n

\"\"

\n

서버에서 제공하는 HTML 코드의 파싱 및 렌더링

\n

마지막 이미지는 브라우저가 HTML 스트리밍을 처리하는 방법이지만, 스트리밍을 위한 DOM 비교 알고리즘을 지원하려면 동일한 작업을 수행해야 합니다.

\n

페이지 변경사항을 사용자에게 최대한 빨리 제공하기 위해 렌더링 작업이 점진적으로 진행됩니다. 이 접근 방식은 페이지의 상호작용 이후 다음 페인트까지(INP) 점수를 더 높게 만듭니다.

\n

청크가 DFS 순서로 도착하더라도, 브라우저는 엘리먼트를 정의하는 모든 정보를 받기 전까지는 렌더링할 수 없습니다.

\n

내비게이션에 대해 이야기했을 때를 기억해보면, 우리는 다음과 같은 예를 들었습니다.

\n
for await (const node of parseHTMLStream(reader)) {\n  // 스트리밍 중에 각 HTML 노드를 완전히 제어할 수 있습니다.\n  console.log(node.nodeName)\n}
\n

이 시점에서, 노드는 항상 DFS 순서로 도착합니다. 하지만 이것만으로는 충분하지 않습니다. 왜냐하면 우리는 DOM 비교 알고리즘 내부의 트리를 살펴봐야 하기 때문입니다. 즉, firstChild, nextSibling, parentNode 등을 방문하여 실제 DOM 노드와 비교해야 합니다.

\n

이를 위해 제가 최근에 오픈소스로 공개한 parse-html-stream 라이브러리는 스트리밍 중에 노드 트리를 순회하는 것을 지원하여 점진적인 렌더링을 수행할 수 있습니다. 노드 트리를 순회하는 동안 라이브러리가 HTML 청크를 도착하는 순서대로 파싱하고, 파싱된 노드가 있을 때 렌더링 변경을 할 수 있게 해줍니다.

\n
import htmlStreamWalker from 'parse-html-stream/walker'\n\n// ...\n\nconst reader = res.body.getReader()\nconst walker = await htmlStreamWalker(reader)\n\n// 루트 노드\nconst rootNode = walker.rootNode\n\n// 스트림 청크를 고려하여 firstChild를 제공\nconst child = await walker.firstChild(rootNode)\n\n// 스트림 청크를 고려하여 nextSibling을 제공\nconst brother = await walker.nextSibling(rootNode)\n\n// 모든 HTML 노드에서 수행 가능\nconst childOfBrother = await walker.firstChild(brother)\n
\n

이 경우 rootNode, child, brother, childOfBrother는 노드입니다. 그리고 우리는 모든 노드 속성에 접근할 수 있습니다. 그러나 스트리밍 청크가 아직 도착하지 않았기 때문에 다음 두 속성이 참이 아닐 수 있음에 유의하세요.

\n\n

이러한 경우를 위해 parse-html-stream walker는 항상 작동하도록 그들을 대체하는 두 메서드를 제공합니다.

\n\n

이러한 함수의 실행 후에도 초기 노드들은 컨텍스트를 잃지 않고 사용될 수 있습니다.

\n

결론

\n

이 글에서는 초기 렌더링을 넘어 HTML 스트리밍의 이점과 실용적인 응용에 대해 이야기했습니다.

\n

우리는 RPC, 뷰 트랜지션 API, DOM 비교 알고리즘 같은 더 기술적인 개념들에 대해 언급했지만, 각각의 주제에 대해 자세히 들어가기보다는 각각에서 HTML 스트리밍을 어떻게 사용하는지에 대해 이야기했습니다. 이 글에서 깊이 다루지 않은 주제에 대해 더 알고 싶다면, 댓글로 달아주시면 주제에 초점을 맞춘 다른 글을 작성하는 데 참고하겠습니다.

\n

또한, parse-html-stream에 대해서도 이야기했는데, 이는 제가 최근에 오픈소스로 공개한 작은 라이브러리로 누구나 사용할 수 있습니다.

\n

마지막으로, 웹은 하이퍼미디어(HTML)를 전송하기 위해 발명되었습니다. 지난 몇 년 동안 JSON과 많은 클라이언트 코드를 사용했지만, 개발을 더 쉽게 하고 매우 적은 자바스크립트 코드로 웹사이트를 더 가볍게 만들 수 있는 하이퍼미디어 주도(Hypermedia-Driven) 응용 프로그램이 더 많이 나오기를 바랍니다. 따라서, 저는 초기 로드를 넘어 HTML 스트리밍의 미래가 매우 밝다고 생각합니다.

\n

참조

\n\n","frontmatter":{"date":"March 21, 2024","title":"(번역) 스트리밍 HTML과 DOM 비교 알고리즘","categories":"translate react","author":"soobing"},"fields":{"slug":"/react/html-node-streaming/"}},"site":{"siteMetadata":{"siteUrl":"https://soobing.github.io","comments":{"utterances":{"repo":"soobing/soobing.github.io"}}}}},"pageContext":{"slug":"/browser/browser-coordinate-size-api/","nextSlug":"/infra/docker-command/","prevSlug":"/react/html-node-streaming/"}}, "staticQueryHashes": ["1073350324","2938748437"]} \ No newline at end of file diff --git a/page-data/index/page-data.json b/page-data/index/page-data.json index fa383f3c..cba0bdef 100644 --- a/page-data/index/page-data.json +++ b/page-data/index/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-pages-index-js", "path": "/", - "result": {"data":{"allMarkdownRemark":{"edges":[{"node":{"id":"ee4fa349-0d3c-533a-9bcf-38d5b6c48497","excerpt":"TL;DR; TypeScript와 ESLint는 “의도적으로 무시”하는 코드에 대해 다른 접근 방식을 가집니다. TypeScript는 언더스코어(_) 접두사로 시작하는 변수를 자동으로 무시하지만, ESLint의 no-unused-vars 규칙은 기본적으로 더 엄격하므로, 언더스코어로 시작하는 변수도 사용되지 않은 것으로 간주하여 오류를 발생시킵니다. 개발자는 ESLint 설정을 통해 “의도적으로 무시”하는 코드에 대해 TypeScript와 유사한 동작을 구현하거나 더 세밀한 제어를 할 수 있습니다. “의도적으로 무시”하는 코드 아래 예시와 같은 코드를 작성해본 경험이 있으신가요? 우리는 가끔 “의도적으로 무시” 하는 것으로 간주하는 코드를 작성해야 할 때가 있습니다. 루프 인덱스 무시: _index는 사용되지 않지만, entries() 메서드의 반환 값을 구조 분해하기 위해 필요 콜백 함수에서 매개변수 무시: _item은 사용되지 않지만, index에 접근하기 위해 선언 구조 분…","frontmatter":{"categories":"framework","title":"‘의도적으로 무시’하는 코드에 대한 TypeScript와 ESLint의 다른 처리 방식","date":"October 26, 2024"},"fields":{"slug":"/typescript-eslint-handling-intentionally-ignored-variables/"}}},{"node":{"id":"87e0c0f2-6c03-513a-99a6-ef53d6389653","excerpt":"Astro는 애플리케이션을 구축이 아닌 콘텐츠에 중점을 두고 설계된 정적 사이트 빌더입니다. 따라서 블로그, 포트폴리오, 이벤트용 마케팅 성격의 사이트 개발에 매우 적합한 프레임워크 입니다. Astro 컴포넌트에 대해 먼저 간략히 알아보고, 어떻게 여러 프레임워크(React, Preact,Svelte, Vue, SolidJS)를 하나의 프로젝트에서 혼합해서 사용할 수 있는지 알아보도록 하겠습니다. Astro 컴포넌트 Astro 컴포넌트는 모든 Astro 프로젝트의 기본 구성 요소입니다. 이는 클라이언트 측 런타임이 없는 HTML 전용 템플릿 컴포넌트로 .astro 파일 확장자 사용합니다. Astro 컴포넌트에 대해 알아야 할 가장 중요한 점은 클라이언트에서 렌더링되지 않는다는 것입니다. HTML로 렌더링되며, 컴포넌트 스크립트는 브라우저로 전송되는 최종 페이지에서 모두 제거됩니다. 구성(스크립트 + 템플릿) .astro 파일은 스크립트와 템플릿으로 구성됩니다. src/compone…","frontmatter":{"categories":"framework","title":"Astro로 알아보는 Selective & Progressive Hydration","date":"October 13, 2024"},"fields":{"slug":"/astro-hydration/"}}},{"node":{"id":"909b8521-890b-5dc7-9c04-3289e2502910","excerpt":"process.env 값을 구조 분해 할당 하셨나요? 우리가 환경변수를 접근할 때 객체의 속성값에 접근할 때 사용하는 점 표기법을 사용하기 때문에 구조 분해 할당을 자연스럽게 사용해도 될 것 같은데요. 이런 경우, 왜 에러가 발생하는지 이번 글을 통해 알아보겠습니다. 문제 상황 다음과 같은 코드가 있다고 가정해 봅시다. 위 코드는 process.env 객체에서 API_KEY와 NEXT_PUBLIC_ANALYTICS_ID 변수를 구조 분해 할당하여 사용하려고 합니다. 그러나 Next.js에서는 이 코드가 제대로 작동하지 않을 수 있고, 오류가 발생할 수 있습니다. 왜 이런 문제가 발생할까요? process.env는 일반적인 자바스크립트 객체와는 다르기 때문입니다. 이는 Node.js 환경에서 환경 변수를 담고 있는 특수한 객체로, 모든 속성이 문자열 형태로 저장됩니다. Next.js는 빌드 타임과 런타임에서 환경 변수에 접근하는 방식이 다르므로, 이로 인해 구조 분해 할당을 사용할 …","frontmatter":{"categories":"react next","title":"환경 변수(process.env)를 구조 분해 할당하면 안되는 이유","date":"June 26, 2024"},"fields":{"slug":"/react/process-env-destructuring-error.md/"}}},{"node":{"id":"7af1f27b-4f32-56a8-8140-5191c8ba3150","excerpt":"원글: https://www.omarileon.me/blog/typescript-as-const 새로운 구문인 “상수 어설션(const assertions)“은 TypeScript 3.4에서 도입되었습니다. 이것은 변수가 변경되지 않을 것(immutable)이고 가능한 엄격한 타입을 제공해야 한다는 것을 타입스크립트에 알려줍니다. 이는 타입의 종류에 따라 다르게 영향을 미치므로, 이 글에서는 상수 어설션을 어떻게 사용하고 왜 유용한지에 대해 설명할 것입니다. 문자열/숫자 문자열이나 숫자에 “as const”를 추가하면 특정 값으로 타입을 좁힐 수 있습니다. 숫자의 경우 문자열 또는 숫자의 경우 일반적으로 “const”를 사용하여 변수를 정의하면 동일한 효과를 얻을 수 있기 때문에 덜 유용합니다. 런타임 안전성이라는 추가 이점도 있습니다. 때로는 변수로 값을 정의하지 않고, 단순히 문자열 리터럴을 사용하고 싶을 수 있습니다. 예를 들어 값을 반환할 때처럼요. 그때 “as const”…","frontmatter":{"categories":"translate typescript","title":"(번역) 타입스크립트에서 'As Const' 이해하기","date":"April 14, 2024"},"fields":{"slug":"/typescript/typescript-as-const/"}}},{"node":{"id":"863af574-c232-55ba-952a-d2a2e6490845","excerpt":"Argument of type ‘string’ is not assignable to parameter of type ‘“apple” | “banana”‘.(2345) 타입스크립트 사용시 때때로 예상치 못한 타입 호환성 문제에 직면하곤 합니다. 엄격한 타입 시스템을 가지고 있는 타입스크립트로 코드를 작성하다 보면 더욱 이러한 문제와 자주 직면합니다. 하지만 다행히도, 타입스크립트는 이러한 문제들을 해결할 수 있는 강력한 도구들을 제공합니다. 이 글에서는 타입 호환성 문제를 해결하는 데 있어 as const와 satisfies라는 두 가지 도구의 사용 방법과 각각의 장점에 대해 알아보겠습니다. 먼저 우리가 자주 마주할 수 있는 상황을 예시 코드로 살펴보겠습니다. 위에 에러 메시지는 함수의 인자로 예상되는 타입과 실제로 전달된 타입이 일치하지 않을 때 발생합니다. 이 경우, printPerson 함수는 \"apple\" 또는 \"banana\"라는 구체적인 문자열 타입을 기대하지만, perso…","frontmatter":{"categories":"typescript","title":"타입스크립트 타입 호환성 문제 해결하기 \"as const vs satisfies\"","date":"March 31, 2024"},"fields":{"slug":"/typescript/as-const-vs-satisfies/"}}},{"node":{"id":"f5dbfa6e-986a-5edb-9bc0-2f30132ce702","excerpt":"원글: https://aralroca.com/blog/html-node-streaming 썸네일 최근 몇 년간 브라우저는 HTML과 자바스크립트를 스트리밍을 지원하기 시작했습니다. 이 글에서는 이에 대한 장점과 브라우저가 자동으로 수행하지 않는 다른 작업들을 통해 스트리밍의 이점을 최대한 활용할 수 있는 방법에 대해 이야기할 것입니다. 스트리밍 HTML 초기 로드하는 동안에는 브라우저가 자동으로 처리하기 때문에 크게 신경 쓸 필요가 없습니다. 스트리밍 하는 동안 HTML 청크를 받으면, 브라우저는 그 콘텐츠를 출력합니다. 서버에서 스트리밍을 활성화하려면 헤더를 조정해야 합니다. 아래는 예시입니다. 그리고 응답에서는 ReadableStream을 사용합니다. 이것은 Bun을 사용한 예시입니다. enqueue 안에 있는 각 문자열은 브라우저가 받게 될 청크입니다. 스트리밍 중 HTML 콘텐츠 변경 성능상의 이점이 많기 때문에 많이 사용되는 방법 중 하나는 스트리밍 되는 동안 HTML 콘…","frontmatter":{"categories":"translate react","title":"(번역) 스트리밍 HTML과 DOM 비교 알고리즘","date":"March 21, 2024"},"fields":{"slug":"/react/html-node-streaming/"}}},{"node":{"id":"e5b56e8b-108e-5455-8e4a-ebbfe2c18f09","excerpt":"프론트 개발을 하다보면 웹 페이지의 다양한 요소들을 직접 제어해야 하는 경우가 있습니다. 이 글에서는 CSSOM의 네 가지 주요 좌표 체계인 Offset, Page, Viewport, Screen에 대해 자세히 알아보고, 위치와 크기 관련 API는 어떤것이 있는지 살펴볼 예정입니다. 이 글을 통해 웹 페이지 내에서 요소의 위치와 크기를 정밀하게 제어하는 데 도움이 되길 바랍니다. 표준 CSSOM 좌표 체계 보통 클라이언트 개발에서 좌표의 원점은 왼쪽 하단이 아닌, 왼쪽 상단 입니다. CSS 객체 모델에서는 Offset, Page, Viewport, Screen 네 가지 표준 좌표 체계가 있습니다. 출처: MDN Coordinate_systems Offset 정의: 오프셋 좌표 체계는 특정 요소의 상대적 위치를 나타내는 데 사용됩니다. 이는 요소의 왼쪽 상단 모서리를 시작점(원점)으로 합니다. 특징: 이 좌표 체계는 요소의 부모 요소를 기준으로 한 상대적 위치를 제공합니다. 마…","frontmatter":{"categories":"browser","title":"브라우저 위치 및 크기 관련 API들","date":"March 03, 2024"},"fields":{"slug":"/browser/browser-coordinate-size-api/"}}},{"node":{"id":"49246cb3-c4b8-5aeb-a4c5-a1b737e89b51","excerpt":"원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939 CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다. 이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다. 읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다. 약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리…","frontmatter":{"categories":"translate a11y","title":"(번역) 접근성을 고려하여 CSS 작성하기","date":"February 18, 2024"},"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}}},{"node":{"id":"4cbf47cf-4a24-5ffd-88c3-bdede11e8a72","excerpt":"Docker 엔진 시작하기/종료하기 도커 엔진: 자동 실행 설정 활성화시, 컴퓨터를 켰을 때 함께 자동으로 실행 Docker Desktop은 도커 엔진 자동으로 실행하도록 설정 되어있음. 리눅스 명령어로 엔진 시작, 종료, 자동 실행 설정 방법 systemctrl: 서비스 시작 및 중지 관련 리눅스 명령어 (system control) 컨테이너: 실행시킬 스크립트가 따로 필요. 컴퓨터를 켰을 때 함께 자동으로 실행 되지 않음. 컨테이너의 기본적인 사용 방법 컨테이너를 다루는 모든 명령어는 docker 명령어로 시작 도커 명령어 구성: docker 커맨드(무엇을 어떻게) 대상 + 옵션, 인자: docker 커맨드(무엇을 어떻게) 옵션 대상 인자 상위 커맨드는 생략 가능하다?! ex) docker run, docker start 커맨드(무엇을 어떻게)는 상위커맨드와 하위커맨드로 나뉘어짐. container 상위 커맨들를 생략하는 경우가 있음. 도커 1.13부터 커맨드가 재편되면서 …","frontmatter":{"categories":"infra","title":"docker에서 자주쓰는 명령어","date":"February 18, 2024"},"fields":{"slug":"/infra/docker-command/"}}},{"node":{"id":"5fc01a99-7197-518c-a9de-60ace2976502","excerpt":"원문: https://storybook.js.org/blog/storybook-react-server-components/ 스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용 리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다. 가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다. 이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 …","frontmatter":{"categories":"translate react","title":"(번역) 리액트 서버 컴포넌트를 위한 스토리북","date":"February 02, 2024"},"fields":{"slug":"/react/storybook-react-server-components/"}}},{"node":{"id":"161feee4-18f2-5161-8900-a9bc5eda4367","excerpt":"지난 글에서 react-query의 hydrate, dehydrate을 통해 서버에서 prefetching 한 데이터 사용하는 방법에 대해서 살펴보았습니다. 서버에서 prefetching 한 데이터 사용하기 오늘은 조금 실용적으로 Next.js 13, 14 버전의 app router에서 react-query를 어떻게 사용하고 세팅하면 좋을지 고민했던 내용에 대해서 이야기해보도록 하겠습니다. 해당 글은 23년 10월에 메이저 버전 업데이트된, Next.js 14와 React-Query 5 를 기준으로 작성된 글입니다. 예시로 보여주는 코드는 저의 next-14-react-query repo에서 확인 할 수 있습니다. 제가 크게 고민했던 문제는 아래 세 가지입니다. Hydrate vs ReactQueryStreamedHydration 두 가지 API 중에 어떤 것을 채택할까? Hydration API 사용 시에 RSC, RCC 모두에서 깔끔한 코드를 유지하려면 queryOption은 …","frontmatter":{"categories":"react react-query","title":"Next.js app router에서 React Query 사용하면서 고민했던 것들","date":"January 07, 2024"},"fields":{"slug":"/react/next-app-router-react-query/"}}},{"node":{"id":"55331269-8b15-516d-aa6d-7eb90d2fedee","excerpt":"원문: https://javascript.plainenglish.io/the-5-most-innovative-proposals-for-javascript-planned-for-2024-22139dd2f546 JavaScript Logo 2024 자바스크립트가 빠른 속도로 발전함에 따라 2024년은 이 프로그래밍 언어에 있어 중요한 해가 될 것입니다. 이 글에서는 개발자들이 자바스크립트 코딩에 접근하는 방식을 재정의하기 위한 최신 제안을 자세히 살펴보겠습니다. 이 글이 유용하길 바랍니다! 시작해 봅시다! 목차 데코레이터 Temporal API 파이프라인 연산자 Error cause 레코드와 튜플 1. 데코레이터 자바스크립트에 데코레이터가 도입된 것은 개발자가 클래스, 메서드, 프로퍼티 및 매개변수의 동작을 조작하고 개선할 수 있는 방법이 크게 발전했음을 의미합니다. 파이썬과 타입스크립트와 같은 다른 프로그래밍 언어에서 유래한 데코레이터는 기능을 추가하거나 수정하는 간결하고 선언적인…","frontmatter":{"categories":"translate javascript","title":"(번역) 2024년 자바스크립트의 5가지 가장 혁신적인 제안","date":"December 20, 2023"},"fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"}}},{"node":{"id":"d77b67b4-3ea0-53b0-b513-8a24dd4da08d","excerpt":"Next.js나 Remix 같은 프레임워크 내에서 React-Query를 사용한다면, 서버 렌더링 될 때 요청 후 응답받은 데이터를 SPA 방식으로 전환되고 나서도 유지할 수 있을까요? 어떻게 가능할까요? React Query의 hydrate와 dehydrate는 서버에서 미리 가져온 데이터를 클라이언트 사이드에서 재사용 할 수 있게 해줍니다. 이번 글을 통해 서버 렌더링과 어떻게 이를 가능하게 하는지 hydrate와 dehydrate에 대해서 알아봅시다. Server Rendering 서버 렌더링은 사용자가 페이지를 로드하는 즉시 볼 수 있는 초기 HTML을 서버에서 생성하는 행위입니다. 이는 페이지 요청 시 즉시 발생할 수 있으며(SSR), 이전 요청이 캐시 되었거나 빌드 시간에 미리 생성(SSG) 할 수도 있습니다. 클라이언트 렌더링 애플리케이션에서는 사용자에게 화면에 콘텐츠를 표시하기 전에 최소 3번의 서버 왕복(roundtrips)이 필요합니다. 서버 렌더링은 위의 과정…","frontmatter":{"categories":"react react-query","title":"서버에서 React Query prefetching 한 데이터 사용하기","date":"December 10, 2023"},"fields":{"slug":"/react/server-rendering-and-react-query/"}}},{"node":{"id":"9c6b2391-e371-545b-b36b-85a35b38a7ac","excerpt":"원문: https://preactjs.com/blog/introducing-signals/ 시그널은 앱이 복잡해져도 빠른 속도를 유지하도록 하는 상태 표현 방식입니다. 시그널은 반응형 원칙에 기반을 두고 있으며, 가상 돔에 최적화된 독특한 구현을 통해 개발자에게 훌륭한 경험을 제공합니다. 본질적으로 시그널은 특정 값을 가지고 있는 .value 속성을 가진 객체입니다. 컴포넌트 내에서 시그널의 value 속성에 접근하면, 그 시그널의 값이 변경될 때 해당 컴포넌트가 자동으로 업데이트됩니다. 이는 간단하고 작성하기 쉬울 뿐만 아니라, 앱이 얼마나 많은 컴포넌트를 가지고 있든 상태 업데이트가 빠르게 유지되도록 보장합니다. 시그널은 기본적으로 빠르며, 백그라운드에서 자동으로 업데이트를 최적화해줍니다. REPL에서 실행 시그널은 훅과 달리 컴포넌트 내부 또는 외부에서 사용할 수 있습니다. 또한 시그널은 훅과 클래스 컴포넌트 모두에서 훌륭하게 작동하므로, 기존 지식을 활용하며 자신의 속도에 …","frontmatter":{"categories":"translate react","title":"(번역) 시그널(Signal)에 대한 소개","date":"November 27, 2023"},"fields":{"slug":"/react/introducing-signals/"}}},{"node":{"id":"5f49b3c9-9991-5b3e-9e49-46ec9a85c02d","excerpt":"원글: https://itnext.io/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components-82e46b5820c 프런트엔드 개발 영역에서는 용어와 패러다임이 때로는 이해하기 어려울 수 있으며 ‘헤드리스 UI’ 또는 ‘헤드리스 컴포넌트’도 이 범주에 속할 수 있습니다. 이러한 용어들이 무엇을 의미하는지 궁금해서 고개를 갸웃거리고, 혼자만 그런 것이 아닙니다. 사실, 혼란스러운 이름에도 불구하고 이러한 개념들은 복잡한 사용자 인터페이스 관리를 상당히 단순화할 수 있는 매력적인 전략입니다. 헤드리스 컴포넌트는 난해해 보일 수 있지만, 그 진정한 힘은 유연성, 재사용 가능성, 그리고 코드베이스의 구성과 깔끔함을 향상시킬 수 있는 능력에 있습니다. 이 글에서는 이 패턴이 정확히 무엇인지, 왜 유용한지, 그리고 인터페이스 디자인에 대한 접근 방식을 어떻게 혁신할 수 있는지에 대해 탐구해 볼 것입니다. …","frontmatter":{"categories":"translate react","title":"(번역) React에서 UI와 로직 분리하기:헤드리스 컴포넌트를 사용한 클린 코드 접근법","date":"September 13, 2023"},"fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"}}},{"node":{"id":"44fd3704-304a-5b2a-8702-af196e247d28","excerpt":"원글: https://github.com/kamranahmedse/design-patterns-for-humans/blob/master/readme.md 역자주: 이 글은 Kamran Ahmed의 글이며, PHP-7 기준으로 작성된 예제 코드를 타입스크립트로 변경하였습니다. logo 🎉 디자인 패턴에 대한 초간단 설명입니다! 🎉 누구든지 혼란스럽게 만들 수 있는 주제입니다. 이 글에서는 최대한 간단한 방식으로 설명함으로써 당신의 기억 속 (그리고 아마도 제 기억)에 각인시키기 위해 노력할 것입니다. 저의 다른 프로젝트도 확인하고 트위터에서 “안녕”이라고 인사해 보세요. 생성 패턴 구조 패턴 행동 패턴 심플 팩토리(Simple Factory) 어댑터(Adapter) 책임 연쇄(Chain of Responsibility) 팩토리 메서드(Factory Method) 브릿지(Bridge) 명령(Command) 추상 팩토리(Abstract Factory) 컴포지트(Composite) 반복자…","frontmatter":{"categories":"translate cs","title":"(번역) 우리들을 위한 디자인 패턴","date":"August 27, 2023"},"fields":{"slug":"/cs/design-patterns-for-humans/"}}},{"node":{"id":"7e56bfba-a382-5266-96c6-f581980fe75d","excerpt":"힙과 스택 메모리의 차이를 살펴보고, 자바스크립트 대표 엔진인 V8의 Resident set 구조와 Minor GC, Major GC 의 동작 알고리즘에 대해서 자세하게 살펴 보도록 하겠습니다. 이 글이 가비지 컬렉터가 어떻게 동작하는지 이해하는데 도움이 되길 바랍니다. V8 메모리 구조 (feat. Resident set) Resident Set은 실행 중인 프로그램의 메모리 관리를 위해 사용되는 용어로, 현재 V8 프로세스가 사용하는 모든 메모리를 나타냅니다. 이것은 JavaScript 코드, 객체, 함수, 변수 등을 저장하는 데 필요한 모든 메모리를 포함합니다. Resident Set은 다음과 같이 여러 세그먼트로 구성됩니다. \n출처: memory management in V8 stack vs heap 메모리 공간은 크게 스택 메모리(Stack memory)와 힙 메모리(Heap memory)로 구분되며, 힙 메모리는 다양한 목적으로 서비스하는 여러 공간으로 더 나누어집니다.…","frontmatter":{"categories":"javascript","title":"자바스크립트 가비지 컬렉션 알고리즘","date":"July 14, 2023"},"fields":{"slug":"/javascript/garbage-collection/"}}},{"node":{"id":"1a5d4b16-06aa-572e-802e-98a3de358758","excerpt":"원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …","frontmatter":{"categories":"translate react react-query","title":"(번역) React Query를 사용하여 서버 상태를 관리하는 방법","date":"June 21, 2023"},"fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"}}},{"node":{"id":"872218ff-8507-5910-8001-bc52d72fca27","excerpt":"원글: https://javascript.plainenglish.io/6-caching-strategies-to-remember-while-designing-cache-system-da058a3757cf 캐시 시스템 관련 용어, 읽기 vs 쓰기 중심의 애플리케이션에서 캐싱 전략, 캐시를 무효화하는 방법 등에 대해서 알려드리겠습니다. 개요 캐싱의 목표는 원본 소스에서 데이터를 가져오는 횟수를 줄여 처리 속도를 높이고 대기 시간을 감소시키는 것입니다. 캐싱은 인메모리 캐싱, 디스크 캐싱, 데이터베이스 캐싱, CDN 캐싱과 같은 다양한 수준의 아키텍처에서 구현될 수 있습니다. 데이터는 각각 고유한 이점이 있는 다양한 기술을 사용하여 캐싱할 수 있습니다. 인메모리 캐싱은 컴퓨터의 주 메모리에 데이터를 저장하여 디스크 저장소 보다 빠른 액세스를 제공합니다. 반면 디스크 캐싱은 하드 디스크에 데이터를 저장하므로 주 메모리보다는 느리지만 원격 소스에서 데이터를 가져오는 것보다 빠릅니다. 데이터…","frontmatter":{"categories":"translate cs","title":"(번역) 캐시 시스템 설계할 때 기억해야 할 6가지 캐싱 전략","date":"June 13, 2023"},"fields":{"slug":"/cs/6-caching-strategies/"}}},{"node":{"id":"7268c9ab-b8e9-5638-aa7a-c6c2e2d4f1a8","excerpt":"ES6에서는 Proxy, Reflect 객체를 통해 메타프로그래밍을 할 수 있습니다. 메타프로그래밍이 무엇인지? 그리고 Proxy와 Reflect에 대해서 알아보도록 하겠습니다. Metaprogramming 메타프로그래밍은 프로그램이 자기 자신을 조작할 수 있는 능력을 말합니다. 이를 통해 프로그램은 동적으로 자기 자신을 변화시키거나, 다른 프로그램을 분석하거나, 새로운 코드를 생성할 수 있습니다. 메타프로그래밍을 통해 코드의 유연성과 재사용성을 높일 수 있으며, 런타임에서 객체의 동작을 수정할 수 있어서 더욱 강력한 코드를 작성할 수 있습니다. 메타프로그래밍에서 가장 중요한 세 가지 개념 Introspection Introspection은 코드가 자기 자신을 검사할 수 있는 능력을 의미합니다. 이를 통해 코드의 내부 구조와 데이터를 검사하거나, 코드의 실행 시점에서 객체의 메소드나 프로퍼티에 접근할 수 있습니다. 자바스크립트에서는 ES6부터 Reflect 객체를 제공하여 Intr…","frontmatter":{"categories":"javascript","title":"Proxy, Reflect와 메타프로그래밍","date":"May 07, 2023"},"fields":{"slug":"/javascript/metaprogramming/"}}},{"node":{"id":"09bde05f-002e-5468-8934-6141fab54678","excerpt":"원문: https://blog.openreplay.com/deep-copying-objects-with-the-structuredclone-api/ Deep Copying Objects with the StructuredClone API 개요: 자바스크립트에서 객체를 복사하는 것은 간단하지 않으며, 이는 잘 알려진 문제입니다. 그러나 이 글에서는 해결책을 제공합니다. StructuredClone API를 사용하면 모든 객체를 간단하고 빠르게 복사할 수 있습니다. 자바스크립트에서 객체가 변수에 저장될 때, 해당 변수는 객체의 참조값을 갖습니다. 이는 변수 자체에 객체를 저장하는 것이 아니라, 객체의 메모리 위치를 나타내는 식별자를 저장한다는 것을 의미합니다. 객체의 복사는 원시 타입과는 다른 방식으로 동작합니다. 얕은 복사(Shallow Copy) vs. 깊은 복사(Deep Copy) 자바스크립트에서 값은 두 가지 방법으로 복사할 수 있습니다. 얕은 복사와 깊은 복사입니다. 얕은 복…","frontmatter":{"categories":"translate javascript","title":"(번역) StructuredClone API를 사용하여 객체를 깊은 복사하는 법","date":"May 07, 2023"},"fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"}}},{"node":{"id":"7dd77d91-4c83-5f76-92b0-9e9b60089ea7","excerpt":"Date와 인연이 깊은지 날짜 선택 UI를 custom 해서 개발해야 하는 경험을 두 번째 하게 되었다. mui datepicker를 붙이기엔 너무 과해서 react-calendar 라이브러리를 사용하려고 했는데, 문제는 input은 함께 딸려오지 않기 때문에 input 관련 interaction은 직접 구현해야 했다. 인 하우스용 서비스라 여러 라이브러리 참고해서 직접 인터렉션을 정의하고 구현해 보았는데 기록 차 경험을 남겨본다! TL;DR; 전체 구현 결과물은 code sandbox에서 확인할 수 있다. DatePicker 컴포넌트를 개발해 보자! (calendar는 라이브러리를 사용) 딱히 input에 대한 큰 요구사항이 없이 기능 개발을 해야 하는 경우 그냥 react-date-picker를 써라 DatePicker 컴포넌트 개발 dependency react-calendar dayjs props 설계 minDate, maxDate: date range를 지원해야하므로 …","frontmatter":{"categories":"feature","title":"날짜 입력 input 인터렉션 개발기","date":"April 23, 2023"},"fields":{"slug":"/feature/input-date/"}}},{"node":{"id":"08e40669-d8eb-531c-ab48-fcad02ba39ef","excerpt":"React 18로 버전업 하면서 프로젝트에 Recoil을 도입했는데, 그 과정에서 고민했던 내용을 기록차 남겨본다.\n TL;DR; 개념 정리나 장단점 비교에 대해서는 이미 알고 계신분들도 있을 것 같아서 폴더구조나 Redux를 대체하는게 맞는가에 대해서만 먼저 간단하게 요약한 내용을 공유한다. 1. 폴더 구조는 data source 별로 나누자 Suggested folder structure for atoms and selectors recoil 폴더 하위에 data source 별로 나누자 data source 하위에 atoms, selectors, hooks를 두자 2. concurrency mode 사용하려면 Redux는 버리고 Recoil로 넘어가야하나? useSyncExternalStore hook 을 사용해서 Redux에서도 동시성 모드를 사용할 수 있다. 이 부분에 대해서 고민해 본 내용은 아래에.. Recoil 도입 배경 Recoil은 Facebook에서 개발한 상태…","frontmatter":{"categories":"react","title":"Recoil 도입기(feat. 폴더구조)","date":"April 08, 2023"},"fields":{"slug":"/react/recoil-introduction/"}}},{"node":{"id":"e19abd3b-8537-528d-a31e-3a74ebbb5cc5","excerpt":"Introduction #1077 이슈를 보면 mutateFn의 variable을 필수값으로 변경한 이력을 볼 수 있다. mutation인데 왜 variable이 없냐고? 나의 경우에는 URL의 있는 queryParams를 읽어와서 데이터를 넘겨주면 되기 때문에 useSomething hook 안에서 처리하고 싶었다. 문제상황 mutate() 와 같이 variable에 아무 값도 넣지 않고 mutation 함수를 호출 할 수 없다.\nTVariables 타입에 undefined도 줘보고 이래저래 별짓을 다해봐도 필수값을 optional 하게 바꿀수는 없었다. 에러 발생\n 해결 실질적으로는 mute 함수에서 variable을 정의해서 넣어주는 것이기 때문에 그렇게 꼼수해결법도 아니다.","frontmatter":{"categories":"react react-query troubleshooting","title":"React Query useMutation에서 variable 옵셔널하게 사용하기","date":"February 15, 2023"},"fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"}}},{"node":{"id":"e1211b4f-4197-5605-879f-45d011d0d210","excerpt":"Introduction 이 글을 읽기전 React의 상태관리 종류 4가지를 먼저 읽으면 이해가 쉬울 수 있다. 얼마전 회사 프로젝트에 react-query를 도입하여 사용중인데 정말 너무너무 좋다. react-query를 이용해서 server state를 분리하다보니 loading 처리나, refetch 등 불필요한 코드가 많이 줄었다. 뭐든지 처음이 어렵지 큰 그림을 알고있으면 도입하여 적용하는데에는 큰 문제가 없고, 사용하면서 상황에 맞게 옵션과 아키텍쳐를 변경하면 된다고 생각하기 때문에, 이번 포스팅에서는 react-query를 적용하기 위해 필요한 전반적인 개념을 정리해보고자 한다. 용도 react-query는 서버 데이터 관리를 위한 라이브러리다. 이 라이브러리는 API 호출, 캐시 및 상태 관리, 오류 처리 등의 작업을 간단하게 처리할 수 있도록 도와준다. 서버 데이터 관리는 중요한 부분 중 하나다. 클라이언트에서는 서버로부터 데이터를 가져와(Read) 화면에 그려주고…","frontmatter":{"categories":"react react-query","title":"React Query 시작하기 (feat. Tanstack)","date":"February 05, 2023"},"fields":{"slug":"/react/react-query-basic/"}}},{"node":{"id":"afca1d77-1bf9-59de-8227-74dfb0b19eed","excerpt":"Introduction react의 상태관리 종류 4가지를 알아보고, 각각의 종류별로 왜 다르게 관리해야하는지와 React에서 제공하는 API 혹은 어떤 라이브러리를 사용해서 관리하면 좋을지 알아보도록 하자. 상태관리 종류 Local state Global state App 어느곳(여러 컴포넌트)에서 state를 확인하거나 업데이트하기 위해서 필요함. 예: authenticated user state - logged in/out Server state 서버로 받은 데이터가 UI 상태와 통합되어야 하는 경우 필요함. 예: loading, error… 대표 라이브러리: SWR, react-query URL state URL에 존재하는 데이터(pathname, query params..) 1) Local state useState useReducer useReducer vs useState 차이 useState는 하나의 state operation만 가능했다면, useReducer는 여…","frontmatter":{"categories":"react","title":"React의 상태관리 종류 4가지","date":"February 03, 2023"},"fields":{"slug":"/react/react-state-management/"}}},{"node":{"id":"0e971c95-1f7f-53c0-b77f-9ed5af3af6e0","excerpt":"고민 혹시 제가 내린 결론에 다른 의견을 주신다면 매우 감사합니다. 🙇🏻‍♀️ 에러를 발생 vs 사전 확인을 통해 예외 처리 어느 부분까지 사전 확인이 필요한 걸까? 고민하게 된 배경 함수의 매개변수가 의도한 것과 다른 값이 들어왔을 때 에러가 발생할 수 있다. 그런데 문득 에러를 발생하게 하는 게 올바른 것인가? 아니면 애초에 에러가 발생할 상황을 하나도 빠짐없이 막는 것이 올바른 것인가? 고민이 들었다. 위의 코드를 보면 event 객체가 무조건 존재할 상황을 가졍하고 있으므로 만약 비정상적인 event 객체가 첫 번째 인자로 전달된다면 에러가 발생할 것이다. 그렇다면 이렇게 코드를 짜는 것이 좋을까? 애초에 event 객체가 존재하지 않을 수 있는 케이스를 고려해서 절대로 에러를 발생하지 않게 하는 것이 좋을까? 결론 App을 Crashed 시키지 않는 한해서 에러가 발생해야 하는 경우는 발생하게 내버려 두고, 예상할 수 있는 예외는 try…catch를 이용하여 에러가 전파되지 않도…","frontmatter":{"categories":"react","title":"에러 핸들링에 대한 고민 (feat. React)","date":"October 16, 2022"},"fields":{"slug":"/react/error-handle/"}}},{"node":{"id":"47a3c1ed-8f39-56be-81c1-c0cd470b4c41","excerpt":"문제상황 dropdown 컴포넌트를 만들었는데, focus가 다른곳으로 이동하면 펼쳐져있던 dropdown이 접혀져야 해서 blur 이벤트 발생시 접히도록 했다. 그랬더니 펼쳐져 있는 항목 중 하나를 click 했을때, 위에서 설정한 blur 이벤트가 먼저 발생하면서 click 이벤트가 호출되지 않는 이슈가 문제였다. dropdown_비정상동작.gif 문제의 코드 TL;DR Touch, mouse 이벤트 순서 touchstart touchmove touchend mouseover mousemove mousedown mouseup click 해결방법 click시 처리해줘야 하는 이벤트 핸들러를 click 이 아닌 mousedown 에서 처리해준다. dropdown_정상동작.gif Dropdown CodeSandbox Touch and mouse 이벤트 그러면 지금부터는 web.dev 문서를 살펴보면서 알게된 touch 이벤트의 도입 배경과 touch, mouse 이벤트를 함께 다루기…","frontmatter":{"categories":"feature browser","title":"Touch, Mouse 이벤트 함께 다루기 (feat. dropdown)","date":"September 18, 2022"},"fields":{"slug":"/browser/touch-mouse-event/"}}},{"node":{"id":"bacf9614-be80-5449-8b45-fa90310ef8dd","excerpt":"목표 Jenkins pipeline을 이용하여, bitbucket에서 PR 생성시 Jenkins job을 실행시키고 그 결과를 slack으로 전송한다. (Jenkins 내의 credential 설정이나 자세한 Jenkins 플러그인 설정 사항은 해당 글에서 포함하지 않음.) Webhook 웹훅이란 뭘까? 위의 목표를 이루려면 우리는 웹훅이 필요하다. 어플리케이션에 한정하여 특정 이벤트가 발생할 때 지정된 URL로 push 알림을 보내는 것이 webhook이다. callback 이랑 비슷한데, 조금 더 작은 범위라고 생각하면 된다. 이 방법은 주기적으로 검사하여 변경 사항이 있는지 확인하는 것보다 훨씬 효율적이다. 구현 방법 우리는 bitbucket에서 PR이 생성되었을때 Jenkins 플러그인인 Generic Webhook Trigger를 호출 하고, Jenkins Job 실행이 끝났을 때 slack Webhook을 호출 할 것이다. 1. Jekins에서 Generic Webho…","frontmatter":{"categories":"infra","title":"Bitbucket과 Jenkins를 연동하여 테스트 자동화 (feat. Generic Webhook Trigger)","date":"September 04, 2022"},"fields":{"slug":"/infra/jenkins-bitbucket/"}}},{"node":{"id":"2b300262-3404-5526-8a92-b3c887f4e178","excerpt":"보통 어플리케이션에서 공통된 디자인의 confirm 창을 사용한다. 이때, window.confirm과 같이 고객의 OK/CANCEL 클릭 여부에 따라서 다음 동작을 이어나가게 하고싶다면 어떻게 구현해야할까? 준비물 Modal Component (global 하게 등록) Global State Modal Hook 보통 웹 어플리케이션에서는 공통된 디자인의 confirm을 사용하므로 화면에 보여지는 역할을 하는 Modal Component가 필요하다. 이 컴포넌트는 어플리케이션 전반에 걸쳐서 자주 사용하는 라이브러리이므로 App.js에서 global 하게 로드한다. global 하게 등록된 Modal 컴포넌트를 제어하기 위해서는 어플리케이션 어느 곳에서든 제어 가능하게 하기 위해서 global state가 필요하다. 그래서 modal 관련 state가 변경되었을때, Modal 컴포넌트가 render 되게 한다. 이제 window.confirm 처럼 사용하기 위해 confirm 함수를…","frontmatter":{"categories":"feature","title":"Promise를 사용하여 window.confirm 구현하기","date":"August 19, 2022"},"fields":{"slug":"/feature/confirm/"}}},{"node":{"id":"34e53d64-0467-56f3-8179-68e3f4d6c48c","excerpt":"🔨 다듬을 필요가 있는 글입니다. 테스트 flow 테스트 코드 작성 → 테스트 읽고 → 실행 → 출력(Reporter) 테스트 종류 단위 테스트: 모듈을 분리된 환경에서 테스트 의존성이 있는 모듈을 제어하기 위해 필연적으로 모의 객체(Mocking)을 사용할 수밖에 없으며, 이 경우 각 모듈이 실제로 잘 연결되어 상호 작용하는지에 대해서는 검증하지 못한다. 통합 테스트: 2개 이상의 모듈 간의 상호작용을 테스트 (개발자 관점의 테스트) 단위 테스트에 비해 mocking을 덜하며, 모듈 간의 연결에서 발생하는 에러를 검증할 수 있다. E2E 테스트: 사용자의 실행 환경과 거의 동일한 환경에서 테스트를 진행 (사용자 관점의 테스트) 세부 모듈들이 갖는 다양한 상황들의 조합을 고려해야 하기 때문에 테스트 자바스크립트 테스트 도구 자바스크립트 테스트는 도구들은 아래와 같이 분류할 수 있다. Test Runners Testing Frameworks Assertion Libraries Tes…","frontmatter":{"categories":"test","title":"자바스크립트 테스트 개념정리","date":"April 14, 2022"},"fields":{"slug":"/test/test-introduction/"}}},{"node":{"id":"44f1621e-8bc3-5e6b-bf8a-29698d901f05","excerpt":"로딩 시작과 끝을 보여주어야 함. role=\"alert\" aria-live=\"polite\"","frontmatter":{"categories":"a11y","title":"로딩 indicator 구현시 고려해야할 접근성","date":"March 31, 2022"},"fields":{"slug":"/a11y/loader/"}}},{"node":{"id":"b89b6ae3-31a1-55cf-ad3e-a3f1573272a5","excerpt":"모달 레이어에 role=\"dialog\" 속성 선언 레이어를 제외한 뒤에 보이는 영역(dimmed 된 영역) aria-hidden=\"true\" aria-hidden=\"true\"로 설정한 영역에 모달이 children으로 들어가서는 안됨. 다른 이유들도 있지만, dialog는 body 태그 밑으로 append 시키는 것이 좋은 이유중에 하나. 모달이 닫히고 초점이 어디로 이동해야 하는가? aria-labeledby 와 id 연결 aria-hidden 값이랑 open 값이랑 연결 참고 https://web.dev/hiding-and-updating-content/ aria-hidden=\"true\"로 설정한 영역에 모달이 children으로 들어가서는 안됨.","frontmatter":{"categories":"a11y","title":"모달 구현시 고려해야할 접근성","date":"March 30, 2022"},"fields":{"slug":"/a11y/modal/"}}},{"node":{"id":"33646ee5-1ede-5c2b-a7d9-23a02bf4c321","excerpt":"get vs find cy.get은 되지만, cy.find는 불가능.\ncy.get.find 이런식으로 체이닝 가능 within cy.get or cy.find로 좁힌 범위 내에서 무언가 실행할때 within이 유용하다. should, and then 사용하는 것보다 should와 and의 조합으로 여러가지 조건을 테스트하는것을 권장함.","frontmatter":{"categories":"test","title":"cypress 주요 api 모음","date":"March 03, 2022"},"fields":{"slug":"/test/cypress-api/"}}},{"node":{"id":"ab3916a6-3a92-5369-b426-953eb81ac6ce","excerpt":"IE에서 새창으로 열어 window 객체에 바인딩된 메서드 호출시, 호출후에 해당 메서드에서 사용했던 object 접근시, object permission denied 에러가 발생하는 이슈가 있다. 결론 = IE에서 새창으로 데이터 주고받아야 하는경우 postMessage를 사용해야 한다. 주의할 점 message 이벤트 리스너를 등록할때 꼭, 필요한 이벤트만 받을 수 있도록\nevent.origin을 체크하도록 한다.\nevent.data를 통해 trigger 된 데이터를 받을 수 있다. 이벤트를 송출할때, target에 '*'을 넣지 않도록 한다.\n왜냐하면 모든 창이 해당 메시지를 다 받을 수 있기 때문에 보안 이슈가있을 수 있다.","frontmatter":{"categories":"browser","title":"postmessage를 이용하여 window 간에 통신하기","date":"February 28, 2022"},"fields":{"slug":"/browser/post-message/"}}},{"node":{"id":"86b158df-f6a8-5534-bbd8-d0f03e33b4da","excerpt":"window.open 으로 새창 열었을때, => scrollable, resizeable, tool 등등 조작가능, windowRef를 반환함.","frontmatter":{"categories":"browser","title":"PC용 브라우저(IE, Safari, Chrome, ...) 이슈 모음","date":"February 23, 2022"},"fields":{"slug":"/browser/pc-issues/"}}},{"node":{"id":"9ab05371-6917-55ab-99e3-f67e4cc1fc2c","excerpt":"","frontmatter":{"categories":"feature","title":"api 서비스 설계","date":"February 23, 2022"},"fields":{"slug":"/feature/api-service/"}}},{"node":{"id":"299a99b0-ddf6-57d2-b677-80e93451598f","excerpt":"IE 11을 대응하면서 겪었던 이슈들 input file 일때 e.target.value 값이 변경되면 onChange를 다시 탄다. text 일때 value 값이 변경되면 onInput 핸들러를 항상 탄다. Date 관련이슈들 Date 생성자 호출시 leadingZeror를 붙이지 않을 경우 Invalid Date 반환 new Date('2021-1') IE에서는 toLocaleDateString 사용시 tokenizer가 /임. (참고로 크롬은 .이다.) 해결방법은 toLocalDateString이 브라우저별로 이슈가 많으므로 getter를 사용한다. ScrollTo 동작 안함. synctax error 백화현상. element.scrollTop = 0 으로 조정해야 함 IE에서 window.open 시 반환하는 윈도우창에 global 변수 바인딩 안됨 IE에서 a tag target=“_blank”로 새창 열 경우 window.opener가 chorme은 null이고, IE는…","frontmatter":{"categories":"browser","title":"Internet Explorer 이슈 모음","date":"February 23, 2022"},"fields":{"slug":"/browser/ie-issues/"}}},{"node":{"id":"4c4c1d5d-db53-54a8-91fa-4b2d276f78af","excerpt":"제 블로그의 테마나 Gatsby의 다른 테마를 활용해서 Github Blog를 만들고 싶은 분들이 계실텐데요! 이런 분들에게 도움을 드리고자 이 글을 쓰게 되었습니다. 잘 안되는 부분이나 궁금한 점을 댓글로 남겨주면 확인해보고 답변 드리도록 하겠습니다! 1. Repository 생성하기 GitHub Blog를 만들려면 Github에 Repository를 생성해야 합니다. GitHub에 로그인 한 뒤에 우측 상단에 있는 New Repository 버튼을 클릭하면 repository 생성 페이지로 이동하게 됩니다. 이 때 Import a repository 버튼을 클릭합니다. 아래 페이지에 도달하시면 두 가지 정보를 넣어주셔야 하는데, Your old repository’s clone URL에는 사용하고자 하는 gatsby 테마가 있는 repository의 주소를 넣어주시면 됩니다. 제 블로그 테마를 쓰고 싶으신 분들은 여기에 https://github.com/zoomKoding/…","frontmatter":{"categories":"블로그","title":"Gatsby 테마로 GitHub Blog 만들기","date":"July 06, 2021"},"fields":{"slug":"/gatsby-github-blog/"}}},{"node":{"id":"203cdfca-2a8d-5db5-9a81-ebef6b08fff3","excerpt":"👋 소개 블로그를 직접 운영하면서 조금씩 그려봤던 이상적인 개발 블로그 테마를 Gatsby를 통해 만들어보게 되었습니다. 이 테마가 블로그를 운영하고자 하시는 분들에게 자신의 이야기를 잘 담을 수 있는 공간이 되었으면 좋겠습니다.🙌 블로그 테마가 맘에 드셨다면 아래 과정을 통해 자신의 블로그를 만들어보시길 바랍니다! 혹시 만드시는 과정에서 궁금하신 점이나 어려움이 있으시다면 이슈를 통해 문의 남겨주세요! 스타는 블로그 테마를 지속적으로 발전시키는데 큰 힘이 됩니다!⭐️ 🚀 시작하기 Github Page나 Netlify 중 원하시는 배포 환경에 따라 다음 과정을 진행하시면 빠르게 블로그를 만드실 수 있습니다. 🦖 GitHub Page로 만들기 깃헙 페이지를 통해 블로그를 만드시다면 아래 글을 참고해주세요! Gatsby 테마로 GitHub Blog 만들기 🔧 Netlify로 만들기 아래 버튼을 활용하면 개인 계정에 zoomkoding-gatsby-blog를 사용하고 있는 Reposito…","frontmatter":{"categories":"블로그","title":"쉽고 빠르게 나만의 개츠비(Gatsby) 블로그 만들기","date":"March 22, 2021"},"fields":{"slug":"/gatsby-starter-zoomkoding-introduction/"}}}]},"site":{"siteMetadata":{"language":"ko","author":{"name":"soobing","bio":{"role":"개발자","description":["지속가능한 소프트웨어를 만드는","배우는 자세를 가진","편하게 소통할 수 있는"]},"social":{"github":"https://github.com/soobing","linkedIn":"https://www.linkedin.com/in/soobin-bak-8a1994117/","email":"qls0147@naver.com"}}}}},"pageContext":{}}, + "result": {"data":{"allMarkdownRemark":{"edges":[{"node":{"id":"ee4fa349-0d3c-533a-9bcf-38d5b6c48497","excerpt":"TL;DR; TypeScript와 ESLint는 “의도적으로 무시”하는 코드에 대해 다른 접근 방식을 가집니다. TypeScript는 언더스코어(_) 접두사로 시작하는 변수를 자동으로 무시하지만, ESLint의 no-unused-vars 규칙은 기본적으로 더 엄격하므로, 언더스코어로 시작하는 변수도 사용되지 않은 것으로 간주하여 오류를 발생시킵니다. 개발자는 ESLint 설정을 통해 “의도적으로 무시”하는 코드에 대해 TypeScript와 유사한 동작을 구현하거나 더 세밀한 제어를 할 수 있습니다. “의도적으로 무시”하는 코드 아래 예시와 같은 코드를 작성해본 경험이 있으신가요? 우리는 가끔 “의도적으로 무시” 하는 것으로 간주하는 코드를 작성해야 할 때가 있습니다. 루프 인덱스 무시: _index는 사용되지 않지만, entries() 메서드의 반환 값을 구조 분해하기 위해 필요 콜백 함수에서 매개변수 무시: _item은 사용되지 않지만, index에 접근하기 위해 선언 구조 분…","frontmatter":{"categories":"framework","title":"‘의도적으로 무시’하는 코드에 대한 TypeScript와 ESLint의 다른 처리 방식","date":"October 26, 2024"},"fields":{"slug":"/typescript-eslint-handling-intentionally-ignored-variables/"}}},{"node":{"id":"87e0c0f2-6c03-513a-99a6-ef53d6389653","excerpt":"Astro는 애플리케이션을 구축이 아닌 콘텐츠에 중점을 두고 설계된 정적 사이트 빌더입니다. 따라서 블로그, 포트폴리오, 이벤트용 마케팅 성격의 사이트 개발에 매우 적합한 프레임워크 입니다. Astro 컴포넌트에 대해 먼저 간략히 알아보고, 어떻게 여러 프레임워크(React, Preact,Svelte, Vue, SolidJS)를 하나의 프로젝트에서 혼합해서 사용할 수 있는지 알아보도록 하겠습니다. Astro 컴포넌트 Astro 컴포넌트는 모든 Astro 프로젝트의 기본 구성 요소입니다. 이는 클라이언트 측 런타임이 없는 HTML 전용 템플릿 컴포넌트로 .astro 파일 확장자 사용합니다. Astro 컴포넌트에 대해 알아야 할 가장 중요한 점은 클라이언트에서 렌더링되지 않는다는 것입니다. HTML로 렌더링되며, 컴포넌트 스크립트는 브라우저로 전송되는 최종 페이지에서 모두 제거됩니다. 구성(스크립트 + 템플릿) .astro 파일은 스크립트와 템플릿으로 구성됩니다. src/compone…","frontmatter":{"categories":"framework","title":"Astro로 알아보는 Selective & Progressive Hydration","date":"October 13, 2024"},"fields":{"slug":"/astro-hydration/"}}},{"node":{"id":"909b8521-890b-5dc7-9c04-3289e2502910","excerpt":"process.env 값을 구조 분해 할당 하셨나요? 우리가 환경변수를 접근할 때 객체의 속성값에 접근할 때 사용하는 점 표기법을 사용하기 때문에 구조 분해 할당을 자연스럽게 사용해도 될 것 같은데요. 이런 경우, 왜 에러가 발생하는지 이번 글을 통해 알아보겠습니다. 문제 상황 다음과 같은 코드가 있다고 가정해 봅시다. 위 코드는 process.env 객체에서 API_KEY와 NEXT_PUBLIC_ANALYTICS_ID 변수를 구조 분해 할당하여 사용하려고 합니다. 그러나 Next.js에서는 이 코드가 제대로 작동하지 않을 수 있고, 오류가 발생할 수 있습니다. 왜 이런 문제가 발생할까요? process.env는 일반적인 자바스크립트 객체와는 다르기 때문입니다. 이는 Node.js 환경에서 환경 변수를 담고 있는 특수한 객체로, 모든 속성이 문자열 형태로 저장됩니다. Next.js는 빌드 타임과 런타임에서 환경 변수에 접근하는 방식이 다르므로, 이로 인해 구조 분해 할당을 사용할 …","frontmatter":{"categories":"react next","title":"환경 변수(process.env)를 구조 분해 할당하면 안되는 이유","date":"June 26, 2024"},"fields":{"slug":"/react/process-env-destructuring-error.md/"}}},{"node":{"id":"7af1f27b-4f32-56a8-8140-5191c8ba3150","excerpt":"원글: https://www.omarileon.me/blog/typescript-as-const 새로운 구문인 “상수 어설션(const assertions)“은 TypeScript 3.4에서 도입되었습니다. 이것은 변수가 변경되지 않을 것(immutable)이고 가능한 엄격한 타입을 제공해야 한다는 것을 타입스크립트에 알려줍니다. 이는 타입의 종류에 따라 다르게 영향을 미치므로, 이 글에서는 상수 어설션을 어떻게 사용하고 왜 유용한지에 대해 설명할 것입니다. 문자열/숫자 문자열이나 숫자에 “as const”를 추가하면 특정 값으로 타입을 좁힐 수 있습니다. 숫자의 경우 문자열 또는 숫자의 경우 일반적으로 “const”를 사용하여 변수를 정의하면 동일한 효과를 얻을 수 있기 때문에 덜 유용합니다. 런타임 안전성이라는 추가 이점도 있습니다. 때로는 변수로 값을 정의하지 않고, 단순히 문자열 리터럴을 사용하고 싶을 수 있습니다. 예를 들어 값을 반환할 때처럼요. 그때 “as const”…","frontmatter":{"categories":"translate typescript","title":"(번역) 타입스크립트에서 'As Const' 이해하기","date":"April 14, 2024"},"fields":{"slug":"/typescript/typescript-as-const/"}}},{"node":{"id":"863af574-c232-55ba-952a-d2a2e6490845","excerpt":"Argument of type ‘string’ is not assignable to parameter of type ‘“apple” | “banana”‘.(2345) 타입스크립트 사용시 때때로 예상치 못한 타입 호환성 문제에 직면하곤 합니다. 엄격한 타입 시스템을 가지고 있는 타입스크립트로 코드를 작성하다 보면 더욱 이러한 문제와 자주 직면합니다. 하지만 다행히도, 타입스크립트는 이러한 문제들을 해결할 수 있는 강력한 도구들을 제공합니다. 이 글에서는 타입 호환성 문제를 해결하는 데 있어 as const와 satisfies라는 두 가지 도구의 사용 방법과 각각의 장점에 대해 알아보겠습니다. 먼저 우리가 자주 마주할 수 있는 상황을 예시 코드로 살펴보겠습니다. 위에 에러 메시지는 함수의 인자로 예상되는 타입과 실제로 전달된 타입이 일치하지 않을 때 발생합니다. 이 경우, printPerson 함수는 \"apple\" 또는 \"banana\"라는 구체적인 문자열 타입을 기대하지만, perso…","frontmatter":{"categories":"typescript","title":"타입스크립트 타입 호환성 문제 해결하기 \"as const vs satisfies\"","date":"March 31, 2024"},"fields":{"slug":"/typescript/as-const-vs-satisfies/"}}},{"node":{"id":"f5dbfa6e-986a-5edb-9bc0-2f30132ce702","excerpt":"원글: https://aralroca.com/blog/html-node-streaming 썸네일 최근 몇 년간 브라우저는 HTML과 자바스크립트를 스트리밍을 지원하기 시작했습니다. 이 글에서는 이에 대한 장점과 브라우저가 자동으로 수행하지 않는 다른 작업들을 통해 스트리밍의 이점을 최대한 활용할 수 있는 방법에 대해 이야기할 것입니다. 스트리밍 HTML 초기 로드하는 동안에는 브라우저가 자동으로 처리하기 때문에 크게 신경 쓸 필요가 없습니다. 스트리밍 하는 동안 HTML 청크를 받으면, 브라우저는 그 콘텐츠를 출력합니다. 서버에서 스트리밍을 활성화하려면 헤더를 조정해야 합니다. 아래는 예시입니다. 그리고 응답에서는 ReadableStream을 사용합니다. 이것은 Bun을 사용한 예시입니다. enqueue 안에 있는 각 문자열은 브라우저가 받게 될 청크입니다. 스트리밍 중 HTML 콘텐츠 변경 성능상의 이점이 많기 때문에 많이 사용되는 방법 중 하나는 스트리밍 되는 동안 HTML 콘…","frontmatter":{"categories":"translate react","title":"(번역) 스트리밍 HTML과 DOM 비교 알고리즘","date":"March 21, 2024"},"fields":{"slug":"/react/html-node-streaming/"}}},{"node":{"id":"e5b56e8b-108e-5455-8e4a-ebbfe2c18f09","excerpt":"프론트 개발을 하다보면 웹 페이지의 다양한 요소들을 직접 제어해야 하는 경우가 있습니다. 이 글에서는 CSSOM의 네 가지 주요 좌표 체계인 Offset, Page, Viewport, Screen에 대해 자세히 알아보고, 위치와 크기 관련 API는 어떤것이 있는지 살펴볼 예정입니다. 이 글을 통해 웹 페이지 내에서 요소의 위치와 크기를 정밀하게 제어하는 데 도움이 되길 바랍니다. 표준 CSSOM 좌표 체계 보통 클라이언트 개발에서 좌표의 원점은 왼쪽 하단이 아닌, 왼쪽 상단 입니다. CSS 객체 모델에서는 Offset, Page, Viewport, Screen 네 가지 표준 좌표 체계가 있습니다. 출처: MDN Coordinate_systems Offset 정의: 오프셋 좌표 체계는 특정 요소의 상대적 위치를 나타내는 데 사용됩니다. 이는 요소의 왼쪽 상단 모서리를 시작점(원점)으로 합니다. 특징: 이 좌표 체계는 요소의 부모 요소를 기준으로 한 상대적 위치를 제공합니다. 마…","frontmatter":{"categories":"browser","title":"브라우저 위치 및 크기 관련 API들","date":"March 03, 2024"},"fields":{"slug":"/browser/browser-coordinate-size-api/"}}},{"node":{"id":"4cbf47cf-4a24-5ffd-88c3-bdede11e8a72","excerpt":"Docker 엔진 시작하기/종료하기 도커 엔진: 자동 실행 설정 활성화시, 컴퓨터를 켰을 때 함께 자동으로 실행 Docker Desktop은 도커 엔진 자동으로 실행하도록 설정 되어있음. 리눅스 명령어로 엔진 시작, 종료, 자동 실행 설정 방법 systemctrl: 서비스 시작 및 중지 관련 리눅스 명령어 (system control) 컨테이너: 실행시킬 스크립트가 따로 필요. 컴퓨터를 켰을 때 함께 자동으로 실행 되지 않음. 컨테이너의 기본적인 사용 방법 컨테이너를 다루는 모든 명령어는 docker 명령어로 시작 도커 명령어 구성: docker 커맨드(무엇을 어떻게) 대상 + 옵션, 인자: docker 커맨드(무엇을 어떻게) 옵션 대상 인자 상위 커맨드는 생략 가능하다?! ex) docker run, docker start 커맨드(무엇을 어떻게)는 상위커맨드와 하위커맨드로 나뉘어짐. container 상위 커맨들를 생략하는 경우가 있음. 도커 1.13부터 커맨드가 재편되면서 …","frontmatter":{"categories":"infra","title":"docker에서 자주쓰는 명령어","date":"February 18, 2024"},"fields":{"slug":"/infra/docker-command/"}}},{"node":{"id":"49246cb3-c4b8-5aeb-a4c5-a1b737e89b51","excerpt":"원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939 CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다. 이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다. 읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다. 약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리…","frontmatter":{"categories":"translate a11y","title":"(번역) 접근성을 고려하여 CSS 작성하기","date":"February 18, 2024"},"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}}},{"node":{"id":"5fc01a99-7197-518c-a9de-60ace2976502","excerpt":"원문: https://storybook.js.org/blog/storybook-react-server-components/ 스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용 리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다. 가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다. 이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 …","frontmatter":{"categories":"translate react","title":"(번역) 리액트 서버 컴포넌트를 위한 스토리북","date":"February 02, 2024"},"fields":{"slug":"/react/storybook-react-server-components/"}}},{"node":{"id":"161feee4-18f2-5161-8900-a9bc5eda4367","excerpt":"지난 글에서 react-query의 hydrate, dehydrate을 통해 서버에서 prefetching 한 데이터 사용하는 방법에 대해서 살펴보았습니다. 서버에서 prefetching 한 데이터 사용하기 오늘은 조금 실용적으로 Next.js 13, 14 버전의 app router에서 react-query를 어떻게 사용하고 세팅하면 좋을지 고민했던 내용에 대해서 이야기해보도록 하겠습니다. 해당 글은 23년 10월에 메이저 버전 업데이트된, Next.js 14와 React-Query 5 를 기준으로 작성된 글입니다. 예시로 보여주는 코드는 저의 next-14-react-query repo에서 확인 할 수 있습니다. 제가 크게 고민했던 문제는 아래 세 가지입니다. Hydrate vs ReactQueryStreamedHydration 두 가지 API 중에 어떤 것을 채택할까? Hydration API 사용 시에 RSC, RCC 모두에서 깔끔한 코드를 유지하려면 queryOption은 …","frontmatter":{"categories":"react react-query","title":"Next.js app router에서 React Query 사용하면서 고민했던 것들","date":"January 07, 2024"},"fields":{"slug":"/react/next-app-router-react-query/"}}},{"node":{"id":"55331269-8b15-516d-aa6d-7eb90d2fedee","excerpt":"원문: https://javascript.plainenglish.io/the-5-most-innovative-proposals-for-javascript-planned-for-2024-22139dd2f546 JavaScript Logo 2024 자바스크립트가 빠른 속도로 발전함에 따라 2024년은 이 프로그래밍 언어에 있어 중요한 해가 될 것입니다. 이 글에서는 개발자들이 자바스크립트 코딩에 접근하는 방식을 재정의하기 위한 최신 제안을 자세히 살펴보겠습니다. 이 글이 유용하길 바랍니다! 시작해 봅시다! 목차 데코레이터 Temporal API 파이프라인 연산자 Error cause 레코드와 튜플 1. 데코레이터 자바스크립트에 데코레이터가 도입된 것은 개발자가 클래스, 메서드, 프로퍼티 및 매개변수의 동작을 조작하고 개선할 수 있는 방법이 크게 발전했음을 의미합니다. 파이썬과 타입스크립트와 같은 다른 프로그래밍 언어에서 유래한 데코레이터는 기능을 추가하거나 수정하는 간결하고 선언적인…","frontmatter":{"categories":"translate javascript","title":"(번역) 2024년 자바스크립트의 5가지 가장 혁신적인 제안","date":"December 20, 2023"},"fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"}}},{"node":{"id":"d77b67b4-3ea0-53b0-b513-8a24dd4da08d","excerpt":"Next.js나 Remix 같은 프레임워크 내에서 React-Query를 사용한다면, 서버 렌더링 될 때 요청 후 응답받은 데이터를 SPA 방식으로 전환되고 나서도 유지할 수 있을까요? 어떻게 가능할까요? React Query의 hydrate와 dehydrate는 서버에서 미리 가져온 데이터를 클라이언트 사이드에서 재사용 할 수 있게 해줍니다. 이번 글을 통해 서버 렌더링과 어떻게 이를 가능하게 하는지 hydrate와 dehydrate에 대해서 알아봅시다. Server Rendering 서버 렌더링은 사용자가 페이지를 로드하는 즉시 볼 수 있는 초기 HTML을 서버에서 생성하는 행위입니다. 이는 페이지 요청 시 즉시 발생할 수 있으며(SSR), 이전 요청이 캐시 되었거나 빌드 시간에 미리 생성(SSG) 할 수도 있습니다. 클라이언트 렌더링 애플리케이션에서는 사용자에게 화면에 콘텐츠를 표시하기 전에 최소 3번의 서버 왕복(roundtrips)이 필요합니다. 서버 렌더링은 위의 과정…","frontmatter":{"categories":"react react-query","title":"서버에서 React Query prefetching 한 데이터 사용하기","date":"December 10, 2023"},"fields":{"slug":"/react/server-rendering-and-react-query/"}}},{"node":{"id":"9c6b2391-e371-545b-b36b-85a35b38a7ac","excerpt":"원문: https://preactjs.com/blog/introducing-signals/ 시그널은 앱이 복잡해져도 빠른 속도를 유지하도록 하는 상태 표현 방식입니다. 시그널은 반응형 원칙에 기반을 두고 있으며, 가상 돔에 최적화된 독특한 구현을 통해 개발자에게 훌륭한 경험을 제공합니다. 본질적으로 시그널은 특정 값을 가지고 있는 .value 속성을 가진 객체입니다. 컴포넌트 내에서 시그널의 value 속성에 접근하면, 그 시그널의 값이 변경될 때 해당 컴포넌트가 자동으로 업데이트됩니다. 이는 간단하고 작성하기 쉬울 뿐만 아니라, 앱이 얼마나 많은 컴포넌트를 가지고 있든 상태 업데이트가 빠르게 유지되도록 보장합니다. 시그널은 기본적으로 빠르며, 백그라운드에서 자동으로 업데이트를 최적화해줍니다. REPL에서 실행 시그널은 훅과 달리 컴포넌트 내부 또는 외부에서 사용할 수 있습니다. 또한 시그널은 훅과 클래스 컴포넌트 모두에서 훌륭하게 작동하므로, 기존 지식을 활용하며 자신의 속도에 …","frontmatter":{"categories":"translate react","title":"(번역) 시그널(Signal)에 대한 소개","date":"November 27, 2023"},"fields":{"slug":"/react/introducing-signals/"}}},{"node":{"id":"5f49b3c9-9991-5b3e-9e49-46ec9a85c02d","excerpt":"원글: https://itnext.io/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components-82e46b5820c 프런트엔드 개발 영역에서는 용어와 패러다임이 때로는 이해하기 어려울 수 있으며 ‘헤드리스 UI’ 또는 ‘헤드리스 컴포넌트’도 이 범주에 속할 수 있습니다. 이러한 용어들이 무엇을 의미하는지 궁금해서 고개를 갸웃거리고, 혼자만 그런 것이 아닙니다. 사실, 혼란스러운 이름에도 불구하고 이러한 개념들은 복잡한 사용자 인터페이스 관리를 상당히 단순화할 수 있는 매력적인 전략입니다. 헤드리스 컴포넌트는 난해해 보일 수 있지만, 그 진정한 힘은 유연성, 재사용 가능성, 그리고 코드베이스의 구성과 깔끔함을 향상시킬 수 있는 능력에 있습니다. 이 글에서는 이 패턴이 정확히 무엇인지, 왜 유용한지, 그리고 인터페이스 디자인에 대한 접근 방식을 어떻게 혁신할 수 있는지에 대해 탐구해 볼 것입니다. …","frontmatter":{"categories":"translate react","title":"(번역) React에서 UI와 로직 분리하기:헤드리스 컴포넌트를 사용한 클린 코드 접근법","date":"September 13, 2023"},"fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"}}},{"node":{"id":"44fd3704-304a-5b2a-8702-af196e247d28","excerpt":"원글: https://github.com/kamranahmedse/design-patterns-for-humans/blob/master/readme.md 역자주: 이 글은 Kamran Ahmed의 글이며, PHP-7 기준으로 작성된 예제 코드를 타입스크립트로 변경하였습니다. logo 🎉 디자인 패턴에 대한 초간단 설명입니다! 🎉 누구든지 혼란스럽게 만들 수 있는 주제입니다. 이 글에서는 최대한 간단한 방식으로 설명함으로써 당신의 기억 속 (그리고 아마도 제 기억)에 각인시키기 위해 노력할 것입니다. 저의 다른 프로젝트도 확인하고 트위터에서 “안녕”이라고 인사해 보세요. 생성 패턴 구조 패턴 행동 패턴 심플 팩토리(Simple Factory) 어댑터(Adapter) 책임 연쇄(Chain of Responsibility) 팩토리 메서드(Factory Method) 브릿지(Bridge) 명령(Command) 추상 팩토리(Abstract Factory) 컴포지트(Composite) 반복자…","frontmatter":{"categories":"translate cs","title":"(번역) 우리들을 위한 디자인 패턴","date":"August 27, 2023"},"fields":{"slug":"/cs/design-patterns-for-humans/"}}},{"node":{"id":"7e56bfba-a382-5266-96c6-f581980fe75d","excerpt":"힙과 스택 메모리의 차이를 살펴보고, 자바스크립트 대표 엔진인 V8의 Resident set 구조와 Minor GC, Major GC 의 동작 알고리즘에 대해서 자세하게 살펴 보도록 하겠습니다. 이 글이 가비지 컬렉터가 어떻게 동작하는지 이해하는데 도움이 되길 바랍니다. V8 메모리 구조 (feat. Resident set) Resident Set은 실행 중인 프로그램의 메모리 관리를 위해 사용되는 용어로, 현재 V8 프로세스가 사용하는 모든 메모리를 나타냅니다. 이것은 JavaScript 코드, 객체, 함수, 변수 등을 저장하는 데 필요한 모든 메모리를 포함합니다. Resident Set은 다음과 같이 여러 세그먼트로 구성됩니다. \n출처: memory management in V8 stack vs heap 메모리 공간은 크게 스택 메모리(Stack memory)와 힙 메모리(Heap memory)로 구분되며, 힙 메모리는 다양한 목적으로 서비스하는 여러 공간으로 더 나누어집니다.…","frontmatter":{"categories":"javascript","title":"자바스크립트 가비지 컬렉션 알고리즘","date":"July 14, 2023"},"fields":{"slug":"/javascript/garbage-collection/"}}},{"node":{"id":"1a5d4b16-06aa-572e-802e-98a3de358758","excerpt":"원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …","frontmatter":{"categories":"translate react react-query","title":"(번역) React Query를 사용하여 서버 상태를 관리하는 방법","date":"June 21, 2023"},"fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"}}},{"node":{"id":"872218ff-8507-5910-8001-bc52d72fca27","excerpt":"원글: https://javascript.plainenglish.io/6-caching-strategies-to-remember-while-designing-cache-system-da058a3757cf 캐시 시스템 관련 용어, 읽기 vs 쓰기 중심의 애플리케이션에서 캐싱 전략, 캐시를 무효화하는 방법 등에 대해서 알려드리겠습니다. 개요 캐싱의 목표는 원본 소스에서 데이터를 가져오는 횟수를 줄여 처리 속도를 높이고 대기 시간을 감소시키는 것입니다. 캐싱은 인메모리 캐싱, 디스크 캐싱, 데이터베이스 캐싱, CDN 캐싱과 같은 다양한 수준의 아키텍처에서 구현될 수 있습니다. 데이터는 각각 고유한 이점이 있는 다양한 기술을 사용하여 캐싱할 수 있습니다. 인메모리 캐싱은 컴퓨터의 주 메모리에 데이터를 저장하여 디스크 저장소 보다 빠른 액세스를 제공합니다. 반면 디스크 캐싱은 하드 디스크에 데이터를 저장하므로 주 메모리보다는 느리지만 원격 소스에서 데이터를 가져오는 것보다 빠릅니다. 데이터…","frontmatter":{"categories":"translate cs","title":"(번역) 캐시 시스템 설계할 때 기억해야 할 6가지 캐싱 전략","date":"June 13, 2023"},"fields":{"slug":"/cs/6-caching-strategies/"}}},{"node":{"id":"7268c9ab-b8e9-5638-aa7a-c6c2e2d4f1a8","excerpt":"ES6에서는 Proxy, Reflect 객체를 통해 메타프로그래밍을 할 수 있습니다. 메타프로그래밍이 무엇인지? 그리고 Proxy와 Reflect에 대해서 알아보도록 하겠습니다. Metaprogramming 메타프로그래밍은 프로그램이 자기 자신을 조작할 수 있는 능력을 말합니다. 이를 통해 프로그램은 동적으로 자기 자신을 변화시키거나, 다른 프로그램을 분석하거나, 새로운 코드를 생성할 수 있습니다. 메타프로그래밍을 통해 코드의 유연성과 재사용성을 높일 수 있으며, 런타임에서 객체의 동작을 수정할 수 있어서 더욱 강력한 코드를 작성할 수 있습니다. 메타프로그래밍에서 가장 중요한 세 가지 개념 Introspection Introspection은 코드가 자기 자신을 검사할 수 있는 능력을 의미합니다. 이를 통해 코드의 내부 구조와 데이터를 검사하거나, 코드의 실행 시점에서 객체의 메소드나 프로퍼티에 접근할 수 있습니다. 자바스크립트에서는 ES6부터 Reflect 객체를 제공하여 Intr…","frontmatter":{"categories":"javascript","title":"Proxy, Reflect와 메타프로그래밍","date":"May 07, 2023"},"fields":{"slug":"/javascript/metaprogramming/"}}},{"node":{"id":"09bde05f-002e-5468-8934-6141fab54678","excerpt":"원문: https://blog.openreplay.com/deep-copying-objects-with-the-structuredclone-api/ Deep Copying Objects with the StructuredClone API 개요: 자바스크립트에서 객체를 복사하는 것은 간단하지 않으며, 이는 잘 알려진 문제입니다. 그러나 이 글에서는 해결책을 제공합니다. StructuredClone API를 사용하면 모든 객체를 간단하고 빠르게 복사할 수 있습니다. 자바스크립트에서 객체가 변수에 저장될 때, 해당 변수는 객체의 참조값을 갖습니다. 이는 변수 자체에 객체를 저장하는 것이 아니라, 객체의 메모리 위치를 나타내는 식별자를 저장한다는 것을 의미합니다. 객체의 복사는 원시 타입과는 다른 방식으로 동작합니다. 얕은 복사(Shallow Copy) vs. 깊은 복사(Deep Copy) 자바스크립트에서 값은 두 가지 방법으로 복사할 수 있습니다. 얕은 복사와 깊은 복사입니다. 얕은 복…","frontmatter":{"categories":"translate javascript","title":"(번역) StructuredClone API를 사용하여 객체를 깊은 복사하는 법","date":"May 07, 2023"},"fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"}}},{"node":{"id":"7dd77d91-4c83-5f76-92b0-9e9b60089ea7","excerpt":"Date와 인연이 깊은지 날짜 선택 UI를 custom 해서 개발해야 하는 경험을 두 번째 하게 되었다. mui datepicker를 붙이기엔 너무 과해서 react-calendar 라이브러리를 사용하려고 했는데, 문제는 input은 함께 딸려오지 않기 때문에 input 관련 interaction은 직접 구현해야 했다. 인 하우스용 서비스라 여러 라이브러리 참고해서 직접 인터렉션을 정의하고 구현해 보았는데 기록 차 경험을 남겨본다! TL;DR; 전체 구현 결과물은 code sandbox에서 확인할 수 있다. DatePicker 컴포넌트를 개발해 보자! (calendar는 라이브러리를 사용) 딱히 input에 대한 큰 요구사항이 없이 기능 개발을 해야 하는 경우 그냥 react-date-picker를 써라 DatePicker 컴포넌트 개발 dependency react-calendar dayjs props 설계 minDate, maxDate: date range를 지원해야하므로 …","frontmatter":{"categories":"feature","title":"날짜 입력 input 인터렉션 개발기","date":"April 23, 2023"},"fields":{"slug":"/feature/input-date/"}}},{"node":{"id":"08e40669-d8eb-531c-ab48-fcad02ba39ef","excerpt":"React 18로 버전업 하면서 프로젝트에 Recoil을 도입했는데, 그 과정에서 고민했던 내용을 기록차 남겨본다.\n TL;DR; 개념 정리나 장단점 비교에 대해서는 이미 알고 계신분들도 있을 것 같아서 폴더구조나 Redux를 대체하는게 맞는가에 대해서만 먼저 간단하게 요약한 내용을 공유한다. 1. 폴더 구조는 data source 별로 나누자 Suggested folder structure for atoms and selectors recoil 폴더 하위에 data source 별로 나누자 data source 하위에 atoms, selectors, hooks를 두자 2. concurrency mode 사용하려면 Redux는 버리고 Recoil로 넘어가야하나? useSyncExternalStore hook 을 사용해서 Redux에서도 동시성 모드를 사용할 수 있다. 이 부분에 대해서 고민해 본 내용은 아래에.. Recoil 도입 배경 Recoil은 Facebook에서 개발한 상태…","frontmatter":{"categories":"react","title":"Recoil 도입기(feat. 폴더구조)","date":"April 08, 2023"},"fields":{"slug":"/react/recoil-introduction/"}}},{"node":{"id":"e19abd3b-8537-528d-a31e-3a74ebbb5cc5","excerpt":"Introduction #1077 이슈를 보면 mutateFn의 variable을 필수값으로 변경한 이력을 볼 수 있다. mutation인데 왜 variable이 없냐고? 나의 경우에는 URL의 있는 queryParams를 읽어와서 데이터를 넘겨주면 되기 때문에 useSomething hook 안에서 처리하고 싶었다. 문제상황 mutate() 와 같이 variable에 아무 값도 넣지 않고 mutation 함수를 호출 할 수 없다.\nTVariables 타입에 undefined도 줘보고 이래저래 별짓을 다해봐도 필수값을 optional 하게 바꿀수는 없었다. 에러 발생\n 해결 실질적으로는 mute 함수에서 variable을 정의해서 넣어주는 것이기 때문에 그렇게 꼼수해결법도 아니다.","frontmatter":{"categories":"react react-query troubleshooting","title":"React Query useMutation에서 variable 옵셔널하게 사용하기","date":"February 15, 2023"},"fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"}}},{"node":{"id":"e1211b4f-4197-5605-879f-45d011d0d210","excerpt":"Introduction 이 글을 읽기전 React의 상태관리 종류 4가지를 먼저 읽으면 이해가 쉬울 수 있다. 얼마전 회사 프로젝트에 react-query를 도입하여 사용중인데 정말 너무너무 좋다. react-query를 이용해서 server state를 분리하다보니 loading 처리나, refetch 등 불필요한 코드가 많이 줄었다. 뭐든지 처음이 어렵지 큰 그림을 알고있으면 도입하여 적용하는데에는 큰 문제가 없고, 사용하면서 상황에 맞게 옵션과 아키텍쳐를 변경하면 된다고 생각하기 때문에, 이번 포스팅에서는 react-query를 적용하기 위해 필요한 전반적인 개념을 정리해보고자 한다. 용도 react-query는 서버 데이터 관리를 위한 라이브러리다. 이 라이브러리는 API 호출, 캐시 및 상태 관리, 오류 처리 등의 작업을 간단하게 처리할 수 있도록 도와준다. 서버 데이터 관리는 중요한 부분 중 하나다. 클라이언트에서는 서버로부터 데이터를 가져와(Read) 화면에 그려주고…","frontmatter":{"categories":"react react-query","title":"React Query 시작하기 (feat. Tanstack)","date":"February 05, 2023"},"fields":{"slug":"/react/react-query-basic/"}}},{"node":{"id":"afca1d77-1bf9-59de-8227-74dfb0b19eed","excerpt":"Introduction react의 상태관리 종류 4가지를 알아보고, 각각의 종류별로 왜 다르게 관리해야하는지와 React에서 제공하는 API 혹은 어떤 라이브러리를 사용해서 관리하면 좋을지 알아보도록 하자. 상태관리 종류 Local state Global state App 어느곳(여러 컴포넌트)에서 state를 확인하거나 업데이트하기 위해서 필요함. 예: authenticated user state - logged in/out Server state 서버로 받은 데이터가 UI 상태와 통합되어야 하는 경우 필요함. 예: loading, error… 대표 라이브러리: SWR, react-query URL state URL에 존재하는 데이터(pathname, query params..) 1) Local state useState useReducer useReducer vs useState 차이 useState는 하나의 state operation만 가능했다면, useReducer는 여…","frontmatter":{"categories":"react","title":"React의 상태관리 종류 4가지","date":"February 03, 2023"},"fields":{"slug":"/react/react-state-management/"}}},{"node":{"id":"0e971c95-1f7f-53c0-b77f-9ed5af3af6e0","excerpt":"고민 혹시 제가 내린 결론에 다른 의견을 주신다면 매우 감사합니다. 🙇🏻‍♀️ 에러를 발생 vs 사전 확인을 통해 예외 처리 어느 부분까지 사전 확인이 필요한 걸까? 고민하게 된 배경 함수의 매개변수가 의도한 것과 다른 값이 들어왔을 때 에러가 발생할 수 있다. 그런데 문득 에러를 발생하게 하는 게 올바른 것인가? 아니면 애초에 에러가 발생할 상황을 하나도 빠짐없이 막는 것이 올바른 것인가? 고민이 들었다. 위의 코드를 보면 event 객체가 무조건 존재할 상황을 가졍하고 있으므로 만약 비정상적인 event 객체가 첫 번째 인자로 전달된다면 에러가 발생할 것이다. 그렇다면 이렇게 코드를 짜는 것이 좋을까? 애초에 event 객체가 존재하지 않을 수 있는 케이스를 고려해서 절대로 에러를 발생하지 않게 하는 것이 좋을까? 결론 App을 Crashed 시키지 않는 한해서 에러가 발생해야 하는 경우는 발생하게 내버려 두고, 예상할 수 있는 예외는 try…catch를 이용하여 에러가 전파되지 않도…","frontmatter":{"categories":"react","title":"에러 핸들링에 대한 고민 (feat. React)","date":"October 16, 2022"},"fields":{"slug":"/react/error-handle/"}}},{"node":{"id":"47a3c1ed-8f39-56be-81c1-c0cd470b4c41","excerpt":"문제상황 dropdown 컴포넌트를 만들었는데, focus가 다른곳으로 이동하면 펼쳐져있던 dropdown이 접혀져야 해서 blur 이벤트 발생시 접히도록 했다. 그랬더니 펼쳐져 있는 항목 중 하나를 click 했을때, 위에서 설정한 blur 이벤트가 먼저 발생하면서 click 이벤트가 호출되지 않는 이슈가 문제였다. dropdown_비정상동작.gif 문제의 코드 TL;DR Touch, mouse 이벤트 순서 touchstart touchmove touchend mouseover mousemove mousedown mouseup click 해결방법 click시 처리해줘야 하는 이벤트 핸들러를 click 이 아닌 mousedown 에서 처리해준다. dropdown_정상동작.gif Dropdown CodeSandbox Touch and mouse 이벤트 그러면 지금부터는 web.dev 문서를 살펴보면서 알게된 touch 이벤트의 도입 배경과 touch, mouse 이벤트를 함께 다루기…","frontmatter":{"categories":"feature browser","title":"Touch, Mouse 이벤트 함께 다루기 (feat. dropdown)","date":"September 18, 2022"},"fields":{"slug":"/browser/touch-mouse-event/"}}},{"node":{"id":"bacf9614-be80-5449-8b45-fa90310ef8dd","excerpt":"목표 Jenkins pipeline을 이용하여, bitbucket에서 PR 생성시 Jenkins job을 실행시키고 그 결과를 slack으로 전송한다. (Jenkins 내의 credential 설정이나 자세한 Jenkins 플러그인 설정 사항은 해당 글에서 포함하지 않음.) Webhook 웹훅이란 뭘까? 위의 목표를 이루려면 우리는 웹훅이 필요하다. 어플리케이션에 한정하여 특정 이벤트가 발생할 때 지정된 URL로 push 알림을 보내는 것이 webhook이다. callback 이랑 비슷한데, 조금 더 작은 범위라고 생각하면 된다. 이 방법은 주기적으로 검사하여 변경 사항이 있는지 확인하는 것보다 훨씬 효율적이다. 구현 방법 우리는 bitbucket에서 PR이 생성되었을때 Jenkins 플러그인인 Generic Webhook Trigger를 호출 하고, Jenkins Job 실행이 끝났을 때 slack Webhook을 호출 할 것이다. 1. Jekins에서 Generic Webho…","frontmatter":{"categories":"infra","title":"Bitbucket과 Jenkins를 연동하여 테스트 자동화 (feat. Generic Webhook Trigger)","date":"September 04, 2022"},"fields":{"slug":"/infra/jenkins-bitbucket/"}}},{"node":{"id":"2b300262-3404-5526-8a92-b3c887f4e178","excerpt":"보통 어플리케이션에서 공통된 디자인의 confirm 창을 사용한다. 이때, window.confirm과 같이 고객의 OK/CANCEL 클릭 여부에 따라서 다음 동작을 이어나가게 하고싶다면 어떻게 구현해야할까? 준비물 Modal Component (global 하게 등록) Global State Modal Hook 보통 웹 어플리케이션에서는 공통된 디자인의 confirm을 사용하므로 화면에 보여지는 역할을 하는 Modal Component가 필요하다. 이 컴포넌트는 어플리케이션 전반에 걸쳐서 자주 사용하는 라이브러리이므로 App.js에서 global 하게 로드한다. global 하게 등록된 Modal 컴포넌트를 제어하기 위해서는 어플리케이션 어느 곳에서든 제어 가능하게 하기 위해서 global state가 필요하다. 그래서 modal 관련 state가 변경되었을때, Modal 컴포넌트가 render 되게 한다. 이제 window.confirm 처럼 사용하기 위해 confirm 함수를…","frontmatter":{"categories":"feature","title":"Promise를 사용하여 window.confirm 구현하기","date":"August 19, 2022"},"fields":{"slug":"/feature/confirm/"}}},{"node":{"id":"34e53d64-0467-56f3-8179-68e3f4d6c48c","excerpt":"🔨 다듬을 필요가 있는 글입니다. 테스트 flow 테스트 코드 작성 → 테스트 읽고 → 실행 → 출력(Reporter) 테스트 종류 단위 테스트: 모듈을 분리된 환경에서 테스트 의존성이 있는 모듈을 제어하기 위해 필연적으로 모의 객체(Mocking)을 사용할 수밖에 없으며, 이 경우 각 모듈이 실제로 잘 연결되어 상호 작용하는지에 대해서는 검증하지 못한다. 통합 테스트: 2개 이상의 모듈 간의 상호작용을 테스트 (개발자 관점의 테스트) 단위 테스트에 비해 mocking을 덜하며, 모듈 간의 연결에서 발생하는 에러를 검증할 수 있다. E2E 테스트: 사용자의 실행 환경과 거의 동일한 환경에서 테스트를 진행 (사용자 관점의 테스트) 세부 모듈들이 갖는 다양한 상황들의 조합을 고려해야 하기 때문에 테스트 자바스크립트 테스트 도구 자바스크립트 테스트는 도구들은 아래와 같이 분류할 수 있다. Test Runners Testing Frameworks Assertion Libraries Tes…","frontmatter":{"categories":"test","title":"자바스크립트 테스트 개념정리","date":"April 14, 2022"},"fields":{"slug":"/test/test-introduction/"}}},{"node":{"id":"44f1621e-8bc3-5e6b-bf8a-29698d901f05","excerpt":"로딩 시작과 끝을 보여주어야 함. role=\"alert\" aria-live=\"polite\"","frontmatter":{"categories":"a11y","title":"로딩 indicator 구현시 고려해야할 접근성","date":"March 31, 2022"},"fields":{"slug":"/a11y/loader/"}}},{"node":{"id":"b89b6ae3-31a1-55cf-ad3e-a3f1573272a5","excerpt":"모달 레이어에 role=\"dialog\" 속성 선언 레이어를 제외한 뒤에 보이는 영역(dimmed 된 영역) aria-hidden=\"true\" aria-hidden=\"true\"로 설정한 영역에 모달이 children으로 들어가서는 안됨. 다른 이유들도 있지만, dialog는 body 태그 밑으로 append 시키는 것이 좋은 이유중에 하나. 모달이 닫히고 초점이 어디로 이동해야 하는가? aria-labeledby 와 id 연결 aria-hidden 값이랑 open 값이랑 연결 참고 https://web.dev/hiding-and-updating-content/ aria-hidden=\"true\"로 설정한 영역에 모달이 children으로 들어가서는 안됨.","frontmatter":{"categories":"a11y","title":"모달 구현시 고려해야할 접근성","date":"March 30, 2022"},"fields":{"slug":"/a11y/modal/"}}},{"node":{"id":"33646ee5-1ede-5c2b-a7d9-23a02bf4c321","excerpt":"get vs find cy.get은 되지만, cy.find는 불가능.\ncy.get.find 이런식으로 체이닝 가능 within cy.get or cy.find로 좁힌 범위 내에서 무언가 실행할때 within이 유용하다. should, and then 사용하는 것보다 should와 and의 조합으로 여러가지 조건을 테스트하는것을 권장함.","frontmatter":{"categories":"test","title":"cypress 주요 api 모음","date":"March 03, 2022"},"fields":{"slug":"/test/cypress-api/"}}},{"node":{"id":"ab3916a6-3a92-5369-b426-953eb81ac6ce","excerpt":"IE에서 새창으로 열어 window 객체에 바인딩된 메서드 호출시, 호출후에 해당 메서드에서 사용했던 object 접근시, object permission denied 에러가 발생하는 이슈가 있다. 결론 = IE에서 새창으로 데이터 주고받아야 하는경우 postMessage를 사용해야 한다. 주의할 점 message 이벤트 리스너를 등록할때 꼭, 필요한 이벤트만 받을 수 있도록\nevent.origin을 체크하도록 한다.\nevent.data를 통해 trigger 된 데이터를 받을 수 있다. 이벤트를 송출할때, target에 '*'을 넣지 않도록 한다.\n왜냐하면 모든 창이 해당 메시지를 다 받을 수 있기 때문에 보안 이슈가있을 수 있다.","frontmatter":{"categories":"browser","title":"postmessage를 이용하여 window 간에 통신하기","date":"February 28, 2022"},"fields":{"slug":"/browser/post-message/"}}},{"node":{"id":"9ab05371-6917-55ab-99e3-f67e4cc1fc2c","excerpt":"","frontmatter":{"categories":"feature","title":"api 서비스 설계","date":"February 23, 2022"},"fields":{"slug":"/feature/api-service/"}}},{"node":{"id":"299a99b0-ddf6-57d2-b677-80e93451598f","excerpt":"IE 11을 대응하면서 겪었던 이슈들 input file 일때 e.target.value 값이 변경되면 onChange를 다시 탄다. text 일때 value 값이 변경되면 onInput 핸들러를 항상 탄다. Date 관련이슈들 Date 생성자 호출시 leadingZeror를 붙이지 않을 경우 Invalid Date 반환 new Date('2021-1') IE에서는 toLocaleDateString 사용시 tokenizer가 /임. (참고로 크롬은 .이다.) 해결방법은 toLocalDateString이 브라우저별로 이슈가 많으므로 getter를 사용한다. ScrollTo 동작 안함. synctax error 백화현상. element.scrollTop = 0 으로 조정해야 함 IE에서 window.open 시 반환하는 윈도우창에 global 변수 바인딩 안됨 IE에서 a tag target=“_blank”로 새창 열 경우 window.opener가 chorme은 null이고, IE는…","frontmatter":{"categories":"browser","title":"Internet Explorer 이슈 모음","date":"February 23, 2022"},"fields":{"slug":"/browser/ie-issues/"}}},{"node":{"id":"86b158df-f6a8-5534-bbd8-d0f03e33b4da","excerpt":"window.open 으로 새창 열었을때, => scrollable, resizeable, tool 등등 조작가능, windowRef를 반환함.","frontmatter":{"categories":"browser","title":"PC용 브라우저(IE, Safari, Chrome, ...) 이슈 모음","date":"February 23, 2022"},"fields":{"slug":"/browser/pc-issues/"}}},{"node":{"id":"4c4c1d5d-db53-54a8-91fa-4b2d276f78af","excerpt":"제 블로그의 테마나 Gatsby의 다른 테마를 활용해서 Github Blog를 만들고 싶은 분들이 계실텐데요! 이런 분들에게 도움을 드리고자 이 글을 쓰게 되었습니다. 잘 안되는 부분이나 궁금한 점을 댓글로 남겨주면 확인해보고 답변 드리도록 하겠습니다! 1. Repository 생성하기 GitHub Blog를 만들려면 Github에 Repository를 생성해야 합니다. GitHub에 로그인 한 뒤에 우측 상단에 있는 New Repository 버튼을 클릭하면 repository 생성 페이지로 이동하게 됩니다. 이 때 Import a repository 버튼을 클릭합니다. 아래 페이지에 도달하시면 두 가지 정보를 넣어주셔야 하는데, Your old repository’s clone URL에는 사용하고자 하는 gatsby 테마가 있는 repository의 주소를 넣어주시면 됩니다. 제 블로그 테마를 쓰고 싶으신 분들은 여기에 https://github.com/zoomKoding/…","frontmatter":{"categories":"블로그","title":"Gatsby 테마로 GitHub Blog 만들기","date":"July 06, 2021"},"fields":{"slug":"/gatsby-github-blog/"}}},{"node":{"id":"203cdfca-2a8d-5db5-9a81-ebef6b08fff3","excerpt":"👋 소개 블로그를 직접 운영하면서 조금씩 그려봤던 이상적인 개발 블로그 테마를 Gatsby를 통해 만들어보게 되었습니다. 이 테마가 블로그를 운영하고자 하시는 분들에게 자신의 이야기를 잘 담을 수 있는 공간이 되었으면 좋겠습니다.🙌 블로그 테마가 맘에 드셨다면 아래 과정을 통해 자신의 블로그를 만들어보시길 바랍니다! 혹시 만드시는 과정에서 궁금하신 점이나 어려움이 있으시다면 이슈를 통해 문의 남겨주세요! 스타는 블로그 테마를 지속적으로 발전시키는데 큰 힘이 됩니다!⭐️ 🚀 시작하기 Github Page나 Netlify 중 원하시는 배포 환경에 따라 다음 과정을 진행하시면 빠르게 블로그를 만드실 수 있습니다. 🦖 GitHub Page로 만들기 깃헙 페이지를 통해 블로그를 만드시다면 아래 글을 참고해주세요! Gatsby 테마로 GitHub Blog 만들기 🔧 Netlify로 만들기 아래 버튼을 활용하면 개인 계정에 zoomkoding-gatsby-blog를 사용하고 있는 Reposito…","frontmatter":{"categories":"블로그","title":"쉽고 빠르게 나만의 개츠비(Gatsby) 블로그 만들기","date":"March 22, 2021"},"fields":{"slug":"/gatsby-starter-zoomkoding-introduction/"}}}]},"site":{"siteMetadata":{"language":"ko","author":{"name":"soobing","bio":{"role":"개발자","description":["지속가능한 소프트웨어를 만드는","배우는 자세를 가진","편하게 소통할 수 있는"]},"social":{"github":"https://github.com/soobing","linkedIn":"https://www.linkedin.com/in/soobin-bak-8a1994117/","email":"qls0147@naver.com"}}}}},"pageContext":{}}, "staticQueryHashes": ["1073350324","2938748437"]} \ No newline at end of file diff --git a/page-data/infra/docker-command/page-data.json b/page-data/infra/docker-command/page-data.json index ebf1fb73..b9c4120d 100644 --- a/page-data/infra/docker-command/page-data.json +++ b/page-data/infra/docker-command/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-blog-template-js", "path": "/infra/docker-command/", - "result": {"data":{"cur":{"id":"4cbf47cf-4a24-5ffd-88c3-bdede11e8a72","html":"

Docker 엔진 시작하기/종료하기

\n\n

컨테이너의 기본적인 사용 방법

\n\n

컨테이너 생성, 삭제, 실행, 정지

\n
docker run (옵션) 이미지 (인자)\ndocker stop 컨테이너_이름\ndocker rm 컨테이너_이름\ndocker ps -a
\n

예제

\n\n","excerpt":"Docker 엔진 시작하기/종료하기 도커 엔진: 자동 실행 설정 활성화시, 컴퓨터를 켰을 때 함께 자동으로 실행 Docker Desktop은 도커 엔진 자동으로 실행하도록 설정 되어있음. 리눅스 명령어로 엔진 시작, 종료, 자동 실행 설정 방법 systemctrl: 서비스 시작 및 중지 관련 리눅스 명령어 (system control) 컨테이너: 실행시킬 스크립트가 따로 필요. 컴퓨터를 켰을 때 함께 자동으로 실행 되지 않음. 컨테이너의 기본적인 사용 방법 컨테이너를 다루는 모든 명령어는 docker 명령어로 시작 도커 명령어 구성: docker 커맨드(무엇을 어떻게) 대상 + 옵션, 인자: docker 커맨드(무엇을 어떻게) 옵션 대상 인자 상위 커맨드는 생략 가능하다?! ex) docker run, docker start 커맨드(무엇을 어떻게)는 상위커맨드와 하위커맨드로 나뉘어짐. container 상위 커맨들를 생략하는 경우가 있음. 도커 1.13부터 커맨드가 재편되면서 …","frontmatter":{"date":"February 18, 2024","title":"docker에서 자주쓰는 명령어","categories":"infra","author":"soobing"},"fields":{"slug":"/infra/docker-command/"}},"next":{"id":"5fc01a99-7197-518c-a9de-60ace2976502","html":"
\n

원문: https://storybook.js.org/blog/storybook-react-server-components/

\n
\n
\n

스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용

\n
\n

리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다.

\n

가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다.

\n

이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 생깁니다.

\n

오늘은 이 질문에 대한 실험적인 답변으로 Next.js 프레임워크에서 스토리북의 RSC 지원을 공식적으로 발표하게 되어 기쁩니다. 이것은 완전히 클라이언트 측 구현이므로, 스토리북 애드온과 인테그레이션의 모든 생태계와 호환이 가능합니다.

\n

이것이 어떻게 작동하는지, 사용하는지, 그리고 지금 바로 사용해 볼 수 있는 방법에 대해 알아보고 싶다면 계속 읽어보세요!

\n

화성에서 온 서버, 금성에서 온 클라이언트

\n

RSC는 기존의 클라이언트 컴포넌트와 두 가지 주요 차이점이 있으며, 다음 예제에서 이에 대해 보여주고 있습니다.

\n
// ApiCard.tsx\n\nimport { ComponentProps } from 'react';\nimport { Card } from './Card';\nimport { findById } from './db';\n\nexport async function DbCard({ id }: {id: number}) {\n  let props;\n  try {\n    const contact = await findById(id);\n    props = { state: 'success', contact };\n  } catch (e) {\n    props = { state: 'error' };\n  }\n  return <Card {...props} />;\n}
\n
    \n
  1. 첫 번째 차이점은 컴포넌트가 async 라는 점인데, 이것은 클라이언트에서는 지원되지 않는 기능입니다.
  2. \n
  3. 두 번째 차이점은 컴포넌트가 Node 코드에 직접 액세스할 수 있다는 것인데, 위의 경우에서는 인증된 데이터베이스 연결을 래핑하는 findById 함수에 액세스하고 있습니다.
  4. \n
\n

RSC는 이러한 두 가지 차이점을 구현하기 위해 내부적으로 많은 작업을 수행합니다. 이 코드는 서버에서만 실행되며 클라이언트로 스트리밍되는 정적 JSON과 유사한 구조를 생성합니다.

\n

Storybook은 순수 클라이언트 애플리케이션입니다. Node가 전혀 없는 순수한 HTML/CSS/JS의 정적 빌드를 생성합니다! 따라서 RSC를 지원하려면 RSC를 클라이언트에서 렌더링하거나 Storybook을 서버용으로 다시 설계하는 방법을 찾아야 합니다.

\n

우리는 먼저 클라이언트 접근 방식에 중점을 두어 시작했습니다. 현재 아키텍처를 기반으로 수백 개의 애드온과 수백만 개의 스토리를 작성한 사용자에게 미치는 영향을 최소화하고 싶습니다.

\n

어떻게 클라이언트에서 동작하게 할 수 있었을까요?

\n

비동기로 처리하기

\n

RSC를 클라이언트에서 렌더링하는 데, 첫 번째 과제는 async 컴포넌트를 지원하는 방법을 알아내는 것입니다. 이미 Next.js의 canary 리액트 버전에서 비공식적으로 지원되고 있습니다. 이 간단한 솔루션에 기여한  JamesManningR 및 julRuss에게 특별한 감사를 전합니다!

\n
import { Suspense } from 'react';\n\nexport const ClientContact = ({ id }) => (\n  <Suspense><DbCard id={id} /></Suspense>\n);
\n

Storybook 8에서부터 @storybook/nextjs.storybook/main.js 파일에서 experimentalNextRSC 기능 플래그를 사용하여 스토리를 Suspense로 감싸줄 수 있습니다.

\n
// .storybook/main.js\nexport default {\n  features: {\n    experimentalNextRSC: true,\n  }\n};
\n

7.x 버전의 @storybook/nextjs에서도 RSC 스토리를 데코레이터로 래핑하여 수동으로 이 작업을 수행할 수도 있습니다.

\n

참고: 이 솔루션은 Next.js의 canary 버전 이외의 다른 Storybook React 프레임워크 (예: react-vitereact-webpack5)에서는 아직 작동하지 않습니다. 다음 버전에서는 이러한 제한이 해제되기를 바랍니다.

\n

모킹 및 로딩

\n

비동기 문제를 해결하는 것은 절반의 성공에 불과합니다. DbCard 컴포넌트는 데이터를 가져와 컴포넌트를 채우는 Node 코드를 참조합니다. 이것은 브라우저에서 Node 코드를 실행할 수 없는 문제가 있습니다!

\n

차선책으로 이 문제를 해결하기 위해서는, 깔끔한 데이터 액세스 계층 구축이 필요합니다. 이것은 또한 RSC의 설계자가 추천하는 최상의 권장 방법입니다.

\n

데이터 액세스 계층이 있으면, 브라우저에서 실행할 수 있도록 모킹할 수 있고 데이터를 정확하게 제어하여 다양한 UI 상태(로딩, 오류, 성공, 등)를 테스트할 수 있습니다.

\n

데이터 액세스 계층을 모킹하기 위해서는 Storybook에서 지원하는 모듈 모킹 또는 네트워크 모킹를 사용할 수 있습니다.

\n

모듈: jest.mock 스타일의 목을 제공하는 storybook-addon-module-mock 커뮤니티 애드온이 있습니다(웹팩 프로젝트에서만 지원). 더 간단하지만 더 제한적인 해결책으로 webpack/vite 별칭을 사용할 수도 있습니다. 향후 버전의 Storybook에서 모듈 모킹을 더 효율적으로 제공할 계획입니다.

\n

네트워크 API: 네트워크 요청을 모킹하기 위해 Mock Service Worker (msw)를 추천합니다. Storybook은 다양한 네트워크GraphQL 모킹 애드온을 지원합니다.

\n

우리의 예제로 돌아와서, storybook-addon-module-mock을 사용한 스토리는 다음과 같습니다.

\n
// DbCard.stories.js\n\nimport { StoryObj, Meta } from '@storybook/react';\nimport { createMock } from 'storybook-addon-module-mock';\n\nimport { DbCard } from './DbCard';\nimport * as db from './db';\n\nexport default { component: DbCard };\n\nexport const Success {\n  args: { id: 1 },\n  parameters: {\n    moduleMock: {\n      mock: () => {\n        const mock = createMock(db, 'findById');\n        mock.mockReturnValue(Promise.resolve({\n          name: 'Beyonce',\n          img: 'https://blackhistorywall.files.wordpress.com/2010/02/picture-device-independent-bitmap-119.jpg',\n          tel: '+123 456 789',\n          email: 'b@beyonce.com'\n        }))\n        return [mock];\n      },\n    },\n  },\n}
\n

API + 모듈 모킹의 전체 데모

\n

데이터베이스의 모듈 모킹 버전과 MSW2로 모킹된 API 버전을 포함한 위의 전체 예제에 대한 전체 내용은 우리의 전체 RSC 데모 Storybook 또는 GitHub 레포지토리를 확인하십시오.

\n

\"\"

\n

주의사항

\n

이 글에서는, Storybook에서 첫 번째 RSC에 대한 스토리를 성공적으로 작성했으며 이 모든 것이 내부적으로 어떻게 구현되었는지 보여주었습니다.

\n

이 모든 것은 꽤 간단한 과정이었지만, 이 접근 방식에는 제한 사항이 있습니다.

\n
    \n
  1. 일치성. 순수한 클라이언트 구현은 애플리케이션에서 실행되는 서버측 스트리밍 RSC 구현과 크게 다릅니다.
  2. \n
  3. 편의성. 여기서 제공하는 모킹 솔루션은 개선될 여지가 있습니다. 현재의 모듈 모킹 솔루션은 장황할 뿐만 아니라 Storybook의 args/controls와 잘 호환되지 않습니다.
  4. \n
\n

우리는 이러한 제한 사항을 향후에 해결할 계획이므로, 이 솔루션을 실험 단계로 표시했습니다.

\n

오늘부터 RSC를 위한 Storybook 사용하기 🎊

\n

RSC를 위한 Storybook을 사용하려면 Storybook을 8.0-alpha로 업그레이드하십시오.

\n
npx storybook@next upgrade --prerelease\n
\n

그런 다음 .storybook/main.ts에서 실험적 기능을 활성화하세요.

\n
// .storybook/main.js\nexport default {\n  features: {\n    experimentalNextRSC: true,\n  },\n};\n
\n

더 많은 정보를 원하시면, @storybook/nextjs README를 참조하세요.

\n

이것은 다음 메이져 버전인 Storybook 8.0의 내용을 자세히 설명하는 첫 번째 게시물이며, 앞으로 몇 달 안에 더 많은 내용이 추가될 예정입니다. 다음 릴리스에 대한 모든 소식을 확인하려면 소셜 미디어에서 팔로우하거나 Storybook 뉴스레터에 가입하세요!

\n","frontmatter":{"date":"February 02, 2024","title":"(번역) 리액트 서버 컴포넌트를 위한 스토리북","categories":"translate react","author":"soobing"},"fields":{"slug":"/react/storybook-react-server-components/"}},"prev":{"id":"49246cb3-c4b8-5aeb-a4c5-a1b737e89b51","html":"
\n

원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939

\n
\n

CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다.

\n

이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다.

\n

읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다.

\n

약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리즈 중 세 번째 글입니다. 관심이 있다면 특별한 순서 없이, 접근성을 고려하여 HTML 작성하기접근성을 고려하여 자바스크립트 작성하기를 지금이나 나중에 읽어보면 좋습니다.

\n
\n

제 첫 웹사이트를 만든 것은 약 17년 전이었고, 그때는 CSS가 아직 상대적으로 새로운 것이었습니다. 그 이후로 많은 것이 변했고, CSS는 이제 우리에게 웹을 스타일링하기 위한 놀라운 도구를 제공합니다. 우리는 Verdana에서 웹폰트로, 고정된 너비에서 반응형 웹 디자인으로, 테이블 기반 레이아웃에서 그리드로 넘어갔고, 이제는 border와 font 또는 shadow에 이미지를 사용할 필요가 없습니다. 우리는 사용자 정의 속성, 쿼리, calc() 및 많은 새로운 단위들을 가지고 있습니다. 물론 이것은 지난 몇 년간의 훌륭한 발전들 중 일부에 불과합니다.

\n

\"\"

\n

접근성을 고려하여 CSS 작성하기

\n

CSS를 사용하여 문제를 해결하는 무한한 방법과 다양한 속성이 우리의 삶을 더 쉽게 만들어주지만, 동시에 사용자의 경험을 악화시킬 수도 있습니다. 사실, 단 세 줄의 CSS 만으로 웹사이트에 접근하기 어렵게 만들 수 있습니다.

\n

이 글에서는 접근성 있는 CSS를 작성하는 데 도움이 될만한 기술과 고려 사항 그리고 접근방식을 모두 모았습니다. 이 컬렉션은 기본 개념과 잘 알려진 속성으로 시작하여, 끝에는 좀 더 새로운 것들을 다룹니다.

\n

예상했던 것보다 많은 내용을 담게 되어, 가장 관심 있는 섹션으로 바로 이동할 수 있도록 링크가 걸린 목차를 마련했습니다.

\n\n

즐겁게 읽어주세요!

\n

🔗 가독성 있는 텍스트에서 읽기 쉬운 텍스트로

\n

이미지, 아이콘, 동영상은 오늘날 웹 디자인에서 빼놓을 수 없는 요소이지만, 여전히 거의 모든 웹사이트에서는 텍스트가 콘텐츠의 대부분을 차지합니다. 텍스트는 어떤 기기에서든 읽을 수 있어야 하기 때문에, 폰트 속성을 스타일링하고, 테스트하며, 미세 조정하는 데 상당한 시간을 할애하는 것이 중요합니다.

\n

글꼴 크기 확대

\n

\"\"

\n

사용자가 화면에서 떨어진 거리에 따라 글꼴 크기는 확대해야 합니다 (출처: Marvel)

\n

한때 12px 글꼴 크기가 본문(body) 텍스트의 표준이었지만, 해상도가 높은 기기의 등장으로 평균 글꼴 크기는 한동안 15에서 18px 사이에 정착했습니다. 최근 몇 년간, 글꼴 크기는 다시 20px 이상으로 상승했으며, 이는 좋은 일입니다. 텍스트는 스마트폰에서 충분히 커야 하며, TV와 같은 큰 화면에서 멀리서도 읽을 수 있도록 화면 크기에 따라 확대해야 합니다.

\n

서체의 특성이 매우 다양하기 때문에 표준의 최소 크기를 정의하는 것은 의미가 없지만, 작은 화면 크기에 좋은 시작점은 아마도 18-20px일 것입니다.

\n

물론 글꼴 크기에 대해 더 많이 말할 수 있지만, 이 글에서 다루기에는 너무 많습니다. 자세한 내용은 Christian Miller당신의 Body 텍스트는 너무 작습니다를 읽어보시길 권장합니다.

\n

라인 높이(line-height) 설정

\n

브라우저의 기본 라인 높이는 대략 **1.2**입니다. 웹 콘텐츠 접근성 지침에 따르면, 텍스트 블록 내의 문단에서는 최소 **1.5**여야 합니다.

\n

\"\"

\n

*line-height가 1.2인 문단과 1.5인 문단 비교*

\n

문단 내 라인 높이가 조정된 텍스트는 가독성이 향상될 뿐만 아니라, 시각적으로도 꽤 더 매력적입니다.

\n

텍스트를 왼쪽 또는 오른쪽으로 정렬

\n

\"\"

\n

양쪽 정렬된 텍스트의 불규칙한 단어 간격

\n

양쪽 정렬이 왼쪽 또는 오른쪽 정렬된 텍스트보다 보기 좋다고 생각하는 사람들도 있지만, 이는 나쁜 관행으로 간주됩니다. text-align: justify는 같은 길이의 줄을 만들기 위해 단어 간격을 조정합니다. 이러한 불균일한 공백은 가독성을 해칠 수 있으며 매우 산만해질 수 있습니다. 필요한 경우 단어를 구분하는 것도 해결책이 될 수 있지만, CSS 하이픈은 잘 지원되지 않고 예상대로 작동하지 않을 수 있습니다.

\n

문단 너비 정의

\n

여러 출처에 따르면 디자이너들은 줄당 45에서 85자를 유지해야 한다고 합니다. 이상적인 문단 너비는 65자라고 여겨집니다.

\n

텍스트 블록의 너비를 정의할 때 ch 단위가 유용할 수 있습니다. 1ch는 숫자 0을 나타내는 문자의 너비와 동일합니다. 또한, font-family 또는 font-size가 변경되면 이에 따라 변경됩니다.

\n
p {\n  /* 최대 너비 65자 */\n  max-width: 65ch;\n}
\n

어떠한 종류의 반응형 타이포그래피 기술을 사용한다면, 매우 큰 화면에서 사이트를 테스트해야 합니다. 글꼴 크기에 제한이 없다면, 특정 뷰포트 크기에서 텍스트가 읽기 어려워질 수 있습니다. 제한을 설정하는 방법이나 반응형 타이포그래피에 익숙하지 않다면, Mike Riethmullers의 글 반응형 타이포그래피에 대한 정밀한 제어를 읽어보시기 바랍니다.

\n

🔗 가상 요소에 콘텐츠 신중하게 사용하기

\n

우리는 ::before::after라는 가상 요소를 사용하여 요소의 맨 처음이나 맨 끝에 CSS를 추가할 수 있습니다. 이것은 디자인 요소를 우리 컴포넌트에 추가하는 매우 일반적이고 편리한 방법을 제공하지만, content 속성을 사용하여 내용을 추가하는 것도 가능합니다. 관심사의 분리의 관점에서 보면, 우리는 이렇게 하지 않아야 합니다.

\n
h2 {\n  content: "DON'T DO THIS";\n}
\n

우리의 내용은 HTML 파일, 데이터베이스 또는 API에서 오는 것이지, CSS에서 오는 것이 아닙니다. 때때로 우리는 폰트 아이콘 또는 특수 문자와 같은 텍스트가 아닌 콘텐츠를 추가하기 위해 content 속성을 사용합니다. 그렇게 할 때, 일부 스크린 리더가 생성된 콘텐츠를 인식하고 설명한다는 것을 기억해야 합니다. 생성된 콘텐츠가 순전히 표현적인 경우 보조 기술에서 숨겨야 합니다. 예를 들어 aria-hidden을 사용할 수 있습니다.

\n
<span class="icon icon-key" aria-hidden="true"></span>
\n

🔗 화면만이 유일한 매체가 아니다

\n

우리가 디지털 시대에 살고 있음에도 불구하고, 사람들은 여전히 물건을 인쇄합니다. 당신의 페이지가 인쇄되거나 PDF로 저장될 때도 접근성이 좋고 사용하기 쉬워야 합니다. 우리가 해야 할 일은 CSS에 @media 블록을 추가하여 종이에 어울리지 않거나 의미가 없는 요소(네비게이션 또는 광고)들을 숨기거나 스타일을 조정하는 것입니다.

\n
@media print {\n  .header {\n    position: static;\n  }  nav {\n    display: none;\n  }\n}
\n

인쇄된 웹 페이지의 문제 중 하나는 링크가 완전히 쓸모없다는 것입니다. 왜냐하면 그것들이 어디로 이끄는지 알 수 없기 때문입니다. 다행히도 CSS는 속성의 값들을 드러내고 화면(이 경우에는 종이)에 표시하는 방법을 제공합니다.

\n
@media print {\n  a[href^="http"]:not([href*="mywebsite.com"])::after {\n    content: " (" attr(href) ")";\n  }\n}
\n

위의 코드는 href 속성이 있고 http로 시작하지만 mywebsite.com이 값에 포함되지 않은 모든 링크 옆에 href 속성의 값을 표시합니다.

\n

Firefox와 특히 Chrome은 인쇄용 스타일 시트를 테스트하고 디버깅하기 위한 도구를 제공합니다.

\n

더 깊이 파고들고 싶다면, 인쇄 스타일 작업을 위한 팁과 트릭들을 모아놓은 것이 있습니다.

\n

🔗 완전히 지원되지 않는 속성 값에 대한 대안

\n

가끔 우리는 특정 속성 값을 사용하고 싶지만, 일부 브라우저에서 지원하지 않기 때문에 사용할 수 없는 상황에 처합니다. 하지만 대안을 제공하는 한, 그것을 사용하는 것을 멈출 필요는 없습니다. 종종 쿼리나 다른 탐지 기능을 사용하지 않고도 할 수 있습니다.

\n

예를 들어, IE와 이전 버전의 Edge가 이해하지 못하는 vmax 단위를 사용하고 싶다고 가정해 봅시다.

\n
div {\n  width: 50vmax; /* IE와 Edge 이전 버전에서 작동하지 않음 */\n}
\n

대안을 제공하기 위해서, 덜 이상적이지만 브라우저가 이해할 수 있는 width 속성을 사용하면 됩니다. 예를 들어 width: 50vw처럼. 다음 줄에서 실제 원하는 값을 설정합니다.

\n
div {\n  width: 50vw;\n  width: 50vmax;\n}
\n

vmax를 이해하지 못하는 브라우저는 width: 50vw를 해석하고 width: 50vmax는 무시합니다. 반면에 vmax를 이해하는 브라우저는 먼저 width: 50vw를 해석한 다음 width: 50vmax를 해석합니다. vmax 선언이 vw 선언 다음에 오기 때문에, 유저는 vmax 로 설정한 버전이 적용 됩니다.

\n

🔗 콘텐츠를 숨기는 여러 가지 방법

\n

HTML의 제목들은 문서(document)의 개요를 잡는 데 매우 유용합니다. <h1>부터 <h6>까지의 제목을 사용함으로써, 브라우저와 다른 소프트웨어에게 문서가 어떻게 구성되어있고 각 부분들이 어떻게 연관되어 있는지 알려줍니다. 문서 개요를 가지는 것은 매우 중요합니다, SEO에 좋고 스크린 리더 사용자들이 사이트를 탐색하는 데 도움이 됩니다. 디자인에 제목이 없어도 제목이 있으면 좋을 것 같은 경우가 발생할 수 있습니다. 그것은 종종 디자인 자체가 구조를 전달할 때의 경우입니다. 이런 경우에는 마크업에서 제목을 단순히 제거하지 않고 시각적으로 숨깁니다. CSS가 있든 없든 문서의 구조가 명확해야 합니다.

\n

이것은 물론 단 하나의 예일 뿐이며, 폼에서 라벨을 시각적으로 숨기는 것은 또 다른 예입니다(UX 관점에서 라벨을 숨기는 것은 바람직하지 않습니다).

\n

CSS에서는 콘텐츠를 숨기는 여러 가지 방법이 있으며, 적절한 기술을 올바른 시나리오에 맞게 선택하는 것은 여러분에게 달려 있습니다.

\n

모든 사람으로부터 콘텐츠 숨기기

\n

hidden 속성을 사용하거나 visibility: hidden 또는 display: none을 설정함으로써 콘텐츠를 완전히 숨길 수 있습니다. 사용자는 볼 수 없으며 스크린 리더나 검색 엔진도 읽을 수 없습니다.

\n

시각적으로만 콘텐츠 숨기기

\n

시각적으로만 콘텐츠를 숨기는 것은 간단하지 않습니다. 스크린 리더는 여전히 접근 가능하게 해야 하며, 브라우저의 특이점을 다뤄야 하고 요소가 포커스될 때 무슨 일이 발생하는지 결정해야 합니다. 물론, 이미 다른 사람들이 이를 해냈고 해결책이 있습니다.

\n

제가 연구를 해본 결과, 많은 다양한 접근 방식이 있다는 것을 알게 되었습니다. 그래서 전문가들에게 의견을 물어보았고, 추천된 기술을 분석하여 무슨 일이 일어나는지 완전히 이해했습니다.

\n
.visually-hidden {\n  /* 일반적인 흐름에서 항목을 제거 */\n  position: absolute;\n  /* 잘못 발음되거나 뭉개지는 텍스트를 위한 해결책 */\n  white-space: nowrap;\n  /* 가능한 가장 작은 크기로 설정 (일부 화면 낭독기는 높이와 너비가 0인 요소를 무시함) */\n  width: 1px;\n  height: 1px;\n  /* 크기 조정 후 넘치는 콘텐츠 숨기기 */\n  overflow: hidden;\n  /* 요소의 크기를 변경할 수 있는 모든 속성 초기화 */\n  border: 0;\n  padding: 0;\n  /* 클리핑은 요소의 어떤 부분이 표시될지 정의함. */\n  /* 구식 clip 속성은 구형 브라우저를 위함 */\n  clip: rect(0 0 0 0);\n  /* 최신 브라우저를 위한 clip-path. inset(50%)는 콘텐츠를 사라지게 하는 내부 사각형을 정의함. */\n  clip-path: inset(50%);\n  /* 현재 아무도 정확히 왜 margin: -1px이 있는지 확실하지 않음. 게다가 이것이 문제를 일으킬 수 있음 (참조:https://github.com/h5bp/html5-boilerplate/issues/1985). */\n  margin: -1px;\n}
\n

이 클래스를 어딘가에 저장하고 시각적으로 콘텐츠를 숨기면서 보조 기술과 검색 엔진은 접근 가능하게 하고 싶을 때 사용하세요.

\n

스킵 링크

\n

앞의 클래스들은 스킵 링크로 사용하기에도 적합합니다. 스킵 링크는 초기에는 시각적으로 숨겨져 있지만 포커스될 때 보이는 링크입니다. 스크린 리더와 키보드 사용자들이 소개 콘텐츠를 건너뛰고 주요 콘텐츠로 바로 이동할 수 있도록 페이지의 첫 번째 항목 중 하나여야 합니다. 기본적으로 사용자를 페이지의 특정 부분으로 이동시키는 앵커 링크입니다.

\n

\"\"

\n

“Skip to content” 링크는 포커스될 때 보입니다

\n

코드 펜에서 직접 시도해보고, Tab을 눌러 건너뛰기 링크를 드러내보세요.

\n

의미론적으로 콘텐츠 숨기기

\n

때때로 시각적으로 콘텐츠를 표시하되 스크린 리더에서는 숨기는 것이 의미가 있습니다. 예를 들어 아이콘을 사용할 때 그렇습니다. 그런 경우에는 숨기고자 하는 요소에 aria-hidden 속성을 추가하고 true로 설정하세요.

\n
<button>\n  <span class="icon icon-hamburger" aria-hidden="true"></span>\n  <span class="text">Menu</span>\n</button>
\n

기타

\n

콘텐츠를 숨기는 다른 방법들도 있습니다. 예를 들어 음수 text-indent나 제로 font-size 또는 height 등입니다. 일부는 작동하지만 특정 주의사항이 있습니다. 자세한 내용은 webaim.org텍스트 숨기기 기술을 읽어보세요.

\n

🔗 나쁜 대비는 신뢰할 수 없다

\n

우리의 디자인은 가독성을 위해 텍스트와 배경 사이에 충분한 대비를 제공해야 합니다. 저시력자뿐만 아니라 시각 장애가 없는 사람들도 고대비에서 이익을 얻습니다. 맑은 날에 스마트폰을 밖에서 사용하는 것을 생각해 보세요.

\n

색상 대비란 무엇이며 왜 중요한가

\n

세계보건기구(World Health Organization)에 따르면 인구의 약 4%가 시각 장애가 있습니다. 남성의 7~12%와 여성의 1% 미만이 어떤 형태의 색상 시력 결함을 가지고 있습니다. 이러한 장애 중 많은 것이 대비에 대한 민감도를 감소시키고, 일부 경우에는 색상을 구별하는 능력까지 줄입니다.

\n

두 색상이 색상 휠의 다른 부분에서 올 때, 그 색상들은 대비를 이룹니다. 일반적으로 말해서, 두 색상의 차이가 클수록 대비가 높습니다. 웹 디자이너 및 개발자로서 우리에게는 단순히 대비 자체뿐만 아니라, 텍스트에 적용됐을 때 얼마나 잘 작동하는지가 중요합니다. 텍스트와 그 배경 사이의 대비는 적어도 중등도 저시력을 가진 사람들이 읽을 수 있을 정도로 높아야 합니다. 물론, 이 기준을 충족하는지 고민할 필요는 없습니다. 웹 접근성 이니셔티브(Web Accessibility Initiative, WAI)는 이를 측정하기 위한 비율을 정의했습니다.

\n

최소 대비 비율

\n

대비 비율은 특정 배경에서 특정 크기와 너비를 가진 텍스트의 대비가 얼마나 높은지를 알려줍니다. 비율은 1:1에서 21:1까지 다양할 수 있습니다. 비교된 두 색상이 동일하면 1:1이고, 검정과 흰색이 대립되는 경우 21:1입니다.

\n

\"\"

\n

#777777 색상의 텍스트가 #DDDDDD 배경에서 3.3:1 비율을 가집니다. (출처: 대비 비율)

\n

웹 콘텐츠 접근성 지침(WCAG) 2.0에 따르면, 배경과 그 텍스트(또는 텍스트 이미지) 사이에 최소 4.5:1의 대비 비율이 존재해야 합니다. 이는 24px 미만(굵지 않은 경우) 및 19px 미만(굵은 경우)인 텍스트에 적용됩니다. 더 큰 텍스트의 경우 3:1 비율이면 충분합니다. 이것들은 레벨 AA 기준을 충족하기 위한 최소 수치입니다. 레벨 AAA를 통과하려면 일반 텍스트의 최소 비율은 7:1이고 굵은 텍스트는 4.5:1입니다. 규정 준수를 위한 필수 사항은 아니지만, 우리가 아이콘을 사용한다면 텍스트 대비 규정을 충족하는 아이콘을 사용해야 합니다.

\n

저는 제 친구 Daniel에게 비율에 대해 말했고, 우리가 현재 작업 중인 프로젝트에서 그것을 올바르게 가져가는 것이 중요하다고 말했습니다. 다양한 조합을 시도한 후, 그는 이것이 생각했던 것보다 어렵다고 전화로 말했습니다. 문제는 충분히 시각적으로 매력적인 조합이 없는 것이 아니라, 지난 몇 년 동안 디자이너들이 저대비 조합을 사용하는 데 익숙해졌다는 것입니다. 작은 에이전시뿐만 아니라 애플이나 구글과 같은 큰 회사들도 이 불리한 디자인 추세를 따르고 있습니다.

\n
\n

나이가 확실히 내 시력에 영향을 미쳤지만, 나는 디자인 트렌드로 고통받고 있다.

\n
\n

Kevin Marks

\n

대비 비율을 계산하기 위한 공식이 있지만, 오래된 계산기를 꺼내서 계산할 필요는 없습니다. 도구들이 있습니다.

\n

대비 비율 측정

\n

Chrome Canary에서는 개발자 도구에서 직접 대비 비율을 표시할 수 있습니다. Remy Sharp가 블로그 글에서 이를 공유합니다.

\n

\"\"

\n

Chrome 개발자 도구에서의 대비 비율.

\n

색상 대비와 일반적인 접근성을 테스트하기 위한 많은 도구들이 있습니다. 다음 목록은 광범위하지는 않지만, 제가 선호하는 도구들의 작은 모음집 입니다.

\n

온라인

\n\n

브라우저에서 빠르고 쉬운 대비 체커

\n\n

브라우저에서 좀 더 많은 옵션을 가진 대비 체커

\n\n

브라우저 도구로 대비와 더 많은 것들을 체크

\n\n

자동 대비 체크가 있는 색상 선택기

\n

브라우저 확장 프로그램과 개발자 도구

\n\n

Chrome 60은 Lighthouse를 기반으로 한 새로운 감사 패널과 함께 출시되었습니다. 접근성 점수를 부여하고 문제를 나열합니다.

\n\n

대비, 문서 개요 등을 테스트하기 위한 훌륭한 브라우저 확장 프로그램.

\n\n

aXe Chrome 확장 프로그램을 사용하여 웹 사이트에서 접근성 결함을 자동으로 찾는 도구.

\n

기타

\n\n

“WCAG에 대한 두 레이어의 색상 대비를 계산하는 Sketch 플러그인.

\n\n

고대비의 경험

\n

고대비의 색상을 사용하는 것도 훌륭하지만, 저시력을 가진 사람들은 여전히 웹사이트에서 사용하는 색상을 변경하고 싶어할 수 있습니다. 사용자의 요구는 매우 다양하며 그에 따라 색상 변경 방법도 다양합니다. 이 사실은 어느 정도 예측 불가능성을 내포하고 있으며, 우리의 페이지들이 항상 완전한 접근성을 보장하기 어렵게 만듭니다. 그래서 우리는 단지, 대비 수준 AA 또는 AAA 기준을 충족하는 것에만 의존해서는 안 되며, 웹사이트를 철저히 테스트하고 대비가 높은 대안을 제공하는 것도 고려해야 합니다.

\n

윈도우에서의 고대비 모드

\n

윈도우에서는 설정에서 고대비 옵션을 사용할 수 있습니다. 사용자는 자신만의 색상 설정을 정의하거나 사전 정의된 테마를 선택할 수 있습니다.

\n

\"\"

\n

윈도우에서의 고대비 설정

\n

간단한 로그인 폼을 만들었고(첫 번째 스크린샷 중 하나. https://dribbble.com/shots/1687064-Simple-Login-Form으로 부터 영감을 받음.) 고대비를 가진 다양한 테마로 테스트해 보았습니다.

\n

\"\"

\n

고대비 설정에서의 다양한 로그인 폼

\n

Anika Henke는 사용자들이 웹사이트에서 색상을 어떻게 변경하는지에 대해 썼습니다. 그녀는 폼을 테스트하던 중 입력 필드가 보이지 않게 되었고 버튼이 인식되지 않게 되었다는 것을 발견했습니다. 위 스크린샷에서도 같은 일이 발생하는 것을 볼 수 있습니다. 대체 텍스트가 없었다면, 사용자들은 두 개의 입력 필드가 있다는 것을 알지 못했을 것입니다. 인풋과 버튼에 기본 테두리를 추가하는 것은 빠른 해결책이었습니다(브라우저 간 테스트되지 않음).

\n

\"\"

\n

고대비 설정에서 인풋과 버튼에 테두리가 있는 개선된 로그인 폼

\n

미디어 쿼리를 사용하여 고대비 모드가 활성화되었는지 감지하고 특정 스타일을 제공할 수 있습니다.

\n
/* 고대비 모드 활성화 */\n@media (-ms-high-contrast:active) {\n}/* 특정 검정-백색 테마의 고대비 모드 */\n@media (-ms-high-contrast:black-on-white) {\n}/* 특정 검정-백색 테마의 고대비 모드 */\n@media (-ms-high-contrast:white-on-black) {\n}
\n

Patrick H. Lauke 는 윈도우 고대비 모드: -ms-high-contrast의 제한된 유용성에서 이 미디어 기능에 대한 그의 생각과 우려를 공유했습니다. 이에 응답으로 Greg Whitworth는 다음과 같이 지적했습니다. “이 기능의 유일한 목적은 대비 민감도를 가진 사용자들에게 더 나은 경험을 제공하는 것입니다. 그러므로, 특정 색상이 무엇인지에 대해 반드시 신경 쓸 필요는 없습니다. 어느 정도까지는, 여러분의 사이트가 어떻게 보이는지보다 어떻게 고대비에서 기능하는지에 대해 신경 써야 합니다.”

\n

높은 대비 크롬 확장 프로그램

\n

구글 크롬을 위한 고대비 확장 프로그램도 있어, 사용자들이 텍스트를 읽기 쉽게 만드는 여러 고대비 색상 필터로 웹을 탐색할 수 있습니다.

\n

고대비의 대안

\n

디자인의 일부분이 충분한 대비를 가지고 있지 않더라도, Alternate Version 조항을 사용하여 WCAG 기준을 충족할 수 있습니다. 이에 따르면, 사용자에게 페이지의 고대비 버전으로 연결하는 링크나, 페이지의 모든 측면이 준수하도록 페이지를 변경할 수 있는 페이지 상의 컨트롤을 제공해야 합니다.

\n

이 대안에 대한 몇 가지 기준이 있습니다.

\n\n

NoCoffee로 테스트하기

\n

\"\"

\n

NoCoffee는 저시력, 색상 결핍 및 차단된 시각 영역을 시뮬레이션합니다

\n

기준을 충족하는 것과 실제 사람을 대상으로 테스트하는 것은 별개의 문제입니다. 모든 사람이 전문적인 테스트 수단을 가지고 있는 것은 아닙니다. 다행히도, NoCoffee는 저시력, 색상 결핍 및 차단된 시각 영역을 쉽게 시뮬레이션할 수 있는 방법을 제공합니다. 이는 경미한~극심한 시력 문제를 가진 사람들이 직면하는 문제를 이해하는 데 도움이 될 수 있습니다.

\n

🔗 색상이 정보의 유일한 단서가 되어서는 안 된다

\n

앞서 언급했듯이, 많은 남성들이 시력 결함을 가지고 있습니다. 유형도 다양합니다. 가장 흔한 유형 중 하나인 중색 이상(Deuteranomaly)은 빨강과 녹색을 구분하기 어렵게 만듭니다. 색상 시력 결함이 있는 사람은 인터페이스를 사용할 수 없게 될 수 있으므로 색상만을 시각적 단서로 사용하는 것을 피해야 합니다.

\n

이전 예시에서 보여준 폼의 입력 필드에 성공 및 오류 상태를 나타내는 테두리를 추가했습니다. 다음 스크린샷은 색상만으로 사용자에게 충분한 피드백을 주지 못한다는 것을 보여줍니다. 테두리 색상이 전혀 보이지 않거나 잘못 보이는 경우가 있습니다.

\n

\"\"

\n

고대비 모드에서 색상만으로 폼의 성공과 실패를 구별하는 것은 효과적이지 않습니다.

\n

간단한 아이콘을 추가하면 접근성과 사용자 경험을 개선하는 데 도움이 될 수 있습니다.

\n

링크도 비슷한 예시 중 하나입니다. 링크는 색상만으로 일반 텍스트와 구별되어서는 안 됩니다. 링크에 밑줄을 유지하는 것이 좋습니다.

\n

🔗 순서에 신경 쓰기

\n

아이템들을 배치 순서를 바꾸는 방법은 많이 있습니다. 예를 들어, Flexbox에는 orderflex-direction이 있고, Grid에는 order, flex-auto-flow 및 명시적 배치가 있습니다. 이러한 속성들은 매우 유용하지만, 콘텐츠의 DOM 순서와 시각적 표현 사이의 연결이 끊어질 수 있습니다.

\n

다음 예시에서는 여러 그리드 속성을 사용하여 배치된 갤러리의 이미지를 볼 수 있습니다.

\n\n

처음에는 문제가 없어 보이지만, 키보드를 사용하여 이미지에서 이미지로 이동할 때 순서를 전혀 예측할 수 없다는 것을 알 수 있습니다. Tab 키를 눌렀을 때 다음에 어떤 이미지가 강조될지 알 수가 없습니다. 여기에 포커스 스타일이 없으면 최악의 상황이 될 수 있습니다.

\n\n

예측 불가능하거나 잘못된 순서는 키보드 사용자뿐만 아니라 스크린 리더 사용자에게도 문제가 됩니다. 스크린 리더는 DOM 순서대로 콘텐츠를 표시하므로 소프트웨어는 CSS 순서에 영향을 받지 않지만 사용자는 영향을 받습니다. 스크린 리더 사용자는 콘텐츠의 시각적 표현에 신경 쓰지 않는다고 생각할 수 있지만, 모든 스크린 리더의 사용자가 시각 장애가 있는 것은 아닙니다. 일부는 저시력이나 학습 장애가 있어 화면에 표시되는 내용을 보완하기 위해 스크린 리더를 사용합니다.

\n

이 순서 문제는 플렉스나 그리드 아이템뿐만 아니라 모든 종류의 위치 지정에도 적용됩니다. 스타일 없이도 의미가 있는 방식으로 콘텐츠를 정렬하는 것이 중요하며, 디자인에서의 순서와 일치하는지 확인해야 합니다. 일치하지 않으면 디자인을 다시 생각해야 할 수 있습니다. CSS에서 올바르게 위치시키지 못한다고 해서 마크업에서 요소들을 임의로 재배열하지 마세요.

\n

Rob Dodson’s의 콘텐츠 재배열이 접근성에 영향을 미치는가?와 Adrian Roselli의 코드의 순서가 중요하다 글을 참고해 더 자세한 내용을 알아보세요.

\n

🔗 중요한 것에 집중하기: focus

\n

저는 이미 키보드 탐색 기초와 포커스 가능한 요소들에 대해 접근성을 고려하여 자바스크립트 작성하기라는 글을 썼습니다. 이 주제가 처음이시라면, 그 글을 빠르게 읽고 오세요.

\n

키보드를 사용하여 웹사이트를 탐색할 수 있도록 하는 것은 중요합니다. 많은 사용자들이 웹 서핑을 할 때 키보드에 의존합니다. 그들 중에는 운동 장애가 있는 사람들, 시각 장애가 있는 사람들, 손이 없거나 어떤 이유로든 마우스나 트랙패드를 사용할 수 없는 사람들이 있습니다.

\n

CSS를 사용해서 포커스 가능한 요소들에 스타일을 적용할 수 있는 몇 가지 방법이 있습니다.

\n

포커스된 아이템 선택하기**

\n

:focus 의사 클래스를 사용하여 포커스 가능한 아이템들이 포커스 되었을때 스타일을 적용할 수 있습니다.

\n
a:focus {\n  background-color: #000000;\n  color: #FFFFFF;\n}
\n

기본 포커스 스타일은 브라우저마다 일관성이 없으며 보기 좋지 않은 경우가 많으며, 때로는 디자인과 잘 어울리지 않습니다. 사용자 경험을 개선하고 디자인에 맞는 맞춤 포커스 스타일을 제공하는 것이 좋습니다.

\n

하지만 무엇을 하든, 대체 스타일을 제공하지 않고 기본 윤곽선(점선 윤곽, 파란색 또는 주황색 반지)만 제거하지 마세요. 주로 키보드로 탐색하는 사용자들은 포커스 위치를 알 수 없다면 사이트를 사용할 수 없게 됩니다.

\n

\"\"

\n

대안 없이 기본 포커스 스타일을 제거하지 마세요 (출처: outlinenone.com)

\n

이것은 단순한 팁이 아니라 레벨 AA 기준 기준입니다.

\n

키보드와 마우스 사용자 구분하기

\n

이미 언급했듯이, 디자이너들이 좌절하는 것 중 하나는 브라우저 간 포커스 스타일의 일관성이 부족하다는 것입니다. 또한 마우스를 사용할 때도 일부 포커스 가능한 요소들에서 포커스 스타일이 보이는 점도 문제입니다. 때로는 마우스 사용자에게는 포커스 스타일을 보여줄 필요가 없으며, 심지어 방해가 되거나 미학적으로 불쾌할 수 있습니다.

\n

\"\"

\n

컨텐츠 영역이 클릭되었을 때 크롬에서 파란색 윤곽선이 보이는 맞춤 탭 컴포넌트 (출처: frend.co)

\n

웹 페이지의 특정 요소에 포커스가 가 있을 때 outline 속성을 제거해서는 안됩니다. 왜냐하면 컴포넌트가 더 이상 키보드 사용자에게 접근 불가능하기 때문입니다. 우리에게 필요한 것은 키보드와 마우스 사용을 구별하는 방법입니다. 이것은 CSS Level 4 선택자 명세의 일부인 :focus-ring 가상 클래스를 사용하여 가능 합니다. ”:focus-ring 가상 클래스는 요소가 :focus 가상 클래스와 일치하고, 사용자 에이전트(브라우저)가 휴리스틱(규칙이나 패턴)을 통해 요소에 특별히 표시되어야 함을 결정할 때 (일반적으로 ‘focus-ring’을 통해) 적용됩니다.” (출처: CSS 선택자 레벨 4 초안)

\n
/* 기본 윤곽선 제거 */\n:focus {\n  outline: none;\n}\n\n/* 윤곽선이 보여야 할 때만 추가 */\n:focus-ring {\n  outline: 2px solid blue;\n}
\n

아쉽게도 현재 어떤 브라우저도 :focus-ring의 표준 구현을 지원하지 않습니다. (Firefox는 -moz-focus-ring을 지원함), 하지만 적절할 때 .focus-ring 클래스를 추가하는 경량 폴리필이 있습니다.

\n
/* 자바스크립트가 활성화되어 작동하고, .focus-ring 클래스가 없는 \n모든 포커스 가능한 요소를 선택하여 윤곽선을 제거합니다 */\n.js-focus-ring :focus:not(.focus-ring) {\n    outline-width: 0;\n}
\n

자세한 내용은 Rob Dodson의 a11ycasts 에피소드, Focus Ring!을 시청하세요.

\n

포커스된 자식이 있는 요소에 대한 스타일링

\n

:focus-within는 상대적으로 새로운 가상 클래스로, 이미 대부분의 주요 브라우저에서 지원되고 있습니다. 이 클래스를 사용하면 현재 포커스된 자식 요소를 가진 요소를 선택할 수 있습니다.

\n

\"\"

\n

자식 항목 중 하나가 포커스되면 그림자가 표시되는 예시입니다.

\n
form:focus-within {\n  box-shadow: 0 0 4px 6px rgba(80,88,156,0.2);\n}
\n

이 기능을 CodePen에서 확인할 수 있습니다.

\n

포커스에 대한 자세한 내용은 YouTube에서 포커스란 무엇인가요? 영상을 참조하세요.

\n

🔗 그리드와 평평한 문서 구조

\n

새로운 사이트를 만들 때, 우리는 보통 HTML 작성부터 시작합니다. 적절한 마크업을 선택하고 요소들을 논리적 순서에 맞게 배치합니다. 문서가 올바르게 작성되어 있고 잘 구조화되어 있으며 순서가 의미 있게 되면 CSS를 추가합니다. CSS Grid Layout이 나오기 전에는, 특히 DOM 순서와 디자인 순서가 일치하지 않은 경우에 레이아웃을 만드는 것이 매우 까다로웠습니다. float, position 그리고 때때로 Flexbox조차도 어떤 상황에서는 충분히 유연하지 않았고, 우리는 DOM 순서를 변경하고 싶은 유혹에 빠지곤 했습니다. Grid의 명시적 배치와 해당 영역 덕분에 아이템을 위치시키는 데 필요한 모든 유연성을 갖게 되었습니다. 이것은 훌륭하지만, Grid는 문서 구조를 해칠 수 있는 새로운 유혹을 도입합니다.

\n

다음과 같은 디자인을 가지고 있고, 그 아이템들에 h2ul을 사용한다고 가정해 봅시다. 왜냐하면 그것이 당신에게 가장 의미가 있기 때문입니다.

\n

\"\"

\n

제목과 목록이 있는 레이아웃

\n
<div class="wrapper">\n  <h2>Heading</h2>\n  <ul>\n    <li><a href="#">Element 1</a></li>\n    <li><a href="#">Element 2</a></li>\n    <li><a href="#">Element 3</a></li>\n    <li><a href="#">Element 4</a></li>\n    <li><a href="#">Element 5</a></li>\n    <li><a href="#">Element 6</a></li>\n  </ul>\n</div>
\n

이러한 요소들을 열에 넣고 <h2>를 위치시키는 것은 꽤 쉽습니다… 또는 적어도 그렇게 보입니다.

\n
.wrapper {\n  display: grid;\n  grid-template-columns: 120px repeat(2, 1fr);\n  grid-gap: 20px;\n}h2 {\n  grid-column: 2 / -1;\n}
\n

\"\"

\n

제목과 목록이 있는 레이아웃. 그리드 컨테이너의 직접적인 자식만 그리드에 배치됩니다.

\n

하지만 예상과는 달리 보입니다. 문제는 그리드 컨테이너의 직접적인 자식만이 그리드에 배치된다는 것인데, 이 경우에는 <h2><ul>이 해당됩니다. 하지만 여러분은 <li>들을 그리드 아이템으로 만들고 싶습니다. 이 문제에 대한 최악의 해결책은 구조를 단순화시켜 <ul>을 제거하고 <li><div>로 변환하여 그리드 컨테이너의 직접적인 자식으로 만드는 것입니다.

\n

가장 좋은 해결책은 <ul>display 속성을 subgrid로 설정하는 것이지만, 불행히도 subgrid는 명세의 레벨 1에 포함되지 않았고 우리는 그것이 출시될 때까지 더 기다려야 합니다.

\n

<ul>display: contents를 사용할 수 있지만, 현재 Firefox만이 이를 지원합니다. display: contents는 요소의 자식들이 마치 요소의 부모의 직접적인 자식인 것처럼 보이게 만듭니다.

\n

결국, **<ul>**에 다른 그리드를 정의해야 합니다. 이것은 이상적이지 않지만, 문서의 구조를 단순화시키고 의미를 해치는 것보다는 낫습니다. 이것은 매우 기본적인 예제이며 목록이 전체 그리드를 차지하기 때문에 부모 그리드에서 일부 값을 상속받을 수 있습니다.

\n
ul {\n  /* 전체 그리드를 차지함 */\n  grid-column: 1 / -1;  /* 다른 그리드를 생성하고 부모 그리드에서 값을 상속받음 */\n  display: inherit;\n  grid-template-columns: inherit;\n  grid-gap: inherit;  /* display: contents를 이해하는 브라우저를 위해 display 덮어쓰기 */\n  display: contents;\n}
\n

CodePen에서 두 가지 솔루션을 볼 수 있습니다.

\n

결론

\n

이 글에서 꽤 많은 내용을 다루고 있지만 CSS와 접근성에 대해 알아야 할 모든 것을 다루지는 않습니다. 그러나 이것은 단순한 출발점 그 이상입니다. DOM 및 포커스 순서를 올바르게 설정하고 고대비에 신경 쓰고 일반적으로 접근성을 고려하여 디자인 하는 것만으로도 이미 훌륭한 작업을 하고 있는 것입니다. 새로운 페이지나 사이트를 만들 때마다 접근성을 약간만 더 고려한다면 웹을 더 나은 곳으로 만들 수 있습니다.

\n
\n

제약사항을 고려하여 디자인하는 것은 단순히 잘 디자인하는 것이다.

\n
\n

Aaron Gustafson

\n

이 글을 즐겁게 읽으셨고 새로운 것을 배우셨기를 바랍니다. 질문이나 피드백이 있으시면 댓글을 남겨주시거나 트위터를 통해 연락해 주시기 바랍니다.

\n

이 글을 작성하는 데 도움을 주신 멘토 Aaron Gustafson에게 감사드립니다.

\n

더 많은 접근성 팁

\n

이 글은 네 부분 시리즈 중 세 번째입니다. 마지막 글은 준비 중이며 곧 공개될 예정입니다.

\n
    \n
  1. 접근성을 고려한 HTML 작성하기
  2. \n
  3. 접근성을 고려한 JavaScript 작성하기
  4. \n
  5. 접근성을 고려한 CSS 작성하기
  6. \n
  7. 다음 글: 접근성을 고려한 디자인 및 개발 방법 배우기
  8. \n
\n

독자 여러분의 독서와 이 글을 좋아하시고 공유해 주시면 감사하겠습니다.

\n

제가 작성한 다른 글도 확인해보실 수 있습니다.

\n

Progressively Enhancing CSS Layout: From Floats To Flexbox To Grid

\n

The Difference Between Explicit and Implicit Grids

\n

추가적인 읽을 거리와 자료들

\n

가독성 있는 텍스트 작성법

\n\n

가상 요소에 콘텐츠 신중하게 사용하기

\n\n

화면만이 유일한 매체가 아니다

\n\n

완전히 지원되지 않는 속성 값에 대한 대안

\n\n

콘텐츠를 숨기는 여러 가지 방법

\n\n

나쁜 대비는 신뢰할 수 없다

\n\n

색상이 정보의 유일한 단서가 되어서는 안 된다

\n\n

순서에 신경 쓰기

\n\n

중요한 것에 집중하기: focus

\n\n

그리드와 평평한 문서 구조

\n\n","frontmatter":{"date":"February 18, 2024","title":"(번역) 접근성을 고려하여 CSS 작성하기","categories":"translate a11y","author":"soobing"},"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}},"site":{"siteMetadata":{"siteUrl":"https://soobing.github.io","comments":{"utterances":{"repo":"soobing/soobing.github.io"}}}}},"pageContext":{"slug":"/infra/docker-command/","nextSlug":"/react/storybook-react-server-components/","prevSlug":"/a11y/writing-css-with-accessibility-in-mind/"}}, + "result": {"data":{"cur":{"id":"4cbf47cf-4a24-5ffd-88c3-bdede11e8a72","html":"

Docker 엔진 시작하기/종료하기

\n\n

컨테이너의 기본적인 사용 방법

\n\n

컨테이너 생성, 삭제, 실행, 정지

\n
docker run (옵션) 이미지 (인자)\ndocker stop 컨테이너_이름\ndocker rm 컨테이너_이름\ndocker ps -a
\n

예제

\n\n","excerpt":"Docker 엔진 시작하기/종료하기 도커 엔진: 자동 실행 설정 활성화시, 컴퓨터를 켰을 때 함께 자동으로 실행 Docker Desktop은 도커 엔진 자동으로 실행하도록 설정 되어있음. 리눅스 명령어로 엔진 시작, 종료, 자동 실행 설정 방법 systemctrl: 서비스 시작 및 중지 관련 리눅스 명령어 (system control) 컨테이너: 실행시킬 스크립트가 따로 필요. 컴퓨터를 켰을 때 함께 자동으로 실행 되지 않음. 컨테이너의 기본적인 사용 방법 컨테이너를 다루는 모든 명령어는 docker 명령어로 시작 도커 명령어 구성: docker 커맨드(무엇을 어떻게) 대상 + 옵션, 인자: docker 커맨드(무엇을 어떻게) 옵션 대상 인자 상위 커맨드는 생략 가능하다?! ex) docker run, docker start 커맨드(무엇을 어떻게)는 상위커맨드와 하위커맨드로 나뉘어짐. container 상위 커맨들를 생략하는 경우가 있음. 도커 1.13부터 커맨드가 재편되면서 …","frontmatter":{"date":"February 18, 2024","title":"docker에서 자주쓰는 명령어","categories":"infra","author":"soobing"},"fields":{"slug":"/infra/docker-command/"}},"next":{"id":"49246cb3-c4b8-5aeb-a4c5-a1b737e89b51","html":"
\n

원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939

\n
\n

CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다.

\n

이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다.

\n

읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다.

\n

약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리즈 중 세 번째 글입니다. 관심이 있다면 특별한 순서 없이, 접근성을 고려하여 HTML 작성하기접근성을 고려하여 자바스크립트 작성하기를 지금이나 나중에 읽어보면 좋습니다.

\n
\n

제 첫 웹사이트를 만든 것은 약 17년 전이었고, 그때는 CSS가 아직 상대적으로 새로운 것이었습니다. 그 이후로 많은 것이 변했고, CSS는 이제 우리에게 웹을 스타일링하기 위한 놀라운 도구를 제공합니다. 우리는 Verdana에서 웹폰트로, 고정된 너비에서 반응형 웹 디자인으로, 테이블 기반 레이아웃에서 그리드로 넘어갔고, 이제는 border와 font 또는 shadow에 이미지를 사용할 필요가 없습니다. 우리는 사용자 정의 속성, 쿼리, calc() 및 많은 새로운 단위들을 가지고 있습니다. 물론 이것은 지난 몇 년간의 훌륭한 발전들 중 일부에 불과합니다.

\n

\"\"

\n

접근성을 고려하여 CSS 작성하기

\n

CSS를 사용하여 문제를 해결하는 무한한 방법과 다양한 속성이 우리의 삶을 더 쉽게 만들어주지만, 동시에 사용자의 경험을 악화시킬 수도 있습니다. 사실, 단 세 줄의 CSS 만으로 웹사이트에 접근하기 어렵게 만들 수 있습니다.

\n

이 글에서는 접근성 있는 CSS를 작성하는 데 도움이 될만한 기술과 고려 사항 그리고 접근방식을 모두 모았습니다. 이 컬렉션은 기본 개념과 잘 알려진 속성으로 시작하여, 끝에는 좀 더 새로운 것들을 다룹니다.

\n

예상했던 것보다 많은 내용을 담게 되어, 가장 관심 있는 섹션으로 바로 이동할 수 있도록 링크가 걸린 목차를 마련했습니다.

\n\n

즐겁게 읽어주세요!

\n

🔗 가독성 있는 텍스트에서 읽기 쉬운 텍스트로

\n

이미지, 아이콘, 동영상은 오늘날 웹 디자인에서 빼놓을 수 없는 요소이지만, 여전히 거의 모든 웹사이트에서는 텍스트가 콘텐츠의 대부분을 차지합니다. 텍스트는 어떤 기기에서든 읽을 수 있어야 하기 때문에, 폰트 속성을 스타일링하고, 테스트하며, 미세 조정하는 데 상당한 시간을 할애하는 것이 중요합니다.

\n

글꼴 크기 확대

\n

\"\"

\n

사용자가 화면에서 떨어진 거리에 따라 글꼴 크기는 확대해야 합니다 (출처: Marvel)

\n

한때 12px 글꼴 크기가 본문(body) 텍스트의 표준이었지만, 해상도가 높은 기기의 등장으로 평균 글꼴 크기는 한동안 15에서 18px 사이에 정착했습니다. 최근 몇 년간, 글꼴 크기는 다시 20px 이상으로 상승했으며, 이는 좋은 일입니다. 텍스트는 스마트폰에서 충분히 커야 하며, TV와 같은 큰 화면에서 멀리서도 읽을 수 있도록 화면 크기에 따라 확대해야 합니다.

\n

서체의 특성이 매우 다양하기 때문에 표준의 최소 크기를 정의하는 것은 의미가 없지만, 작은 화면 크기에 좋은 시작점은 아마도 18-20px일 것입니다.

\n

물론 글꼴 크기에 대해 더 많이 말할 수 있지만, 이 글에서 다루기에는 너무 많습니다. 자세한 내용은 Christian Miller당신의 Body 텍스트는 너무 작습니다를 읽어보시길 권장합니다.

\n

라인 높이(line-height) 설정

\n

브라우저의 기본 라인 높이는 대략 **1.2**입니다. 웹 콘텐츠 접근성 지침에 따르면, 텍스트 블록 내의 문단에서는 최소 **1.5**여야 합니다.

\n

\"\"

\n

*line-height가 1.2인 문단과 1.5인 문단 비교*

\n

문단 내 라인 높이가 조정된 텍스트는 가독성이 향상될 뿐만 아니라, 시각적으로도 꽤 더 매력적입니다.

\n

텍스트를 왼쪽 또는 오른쪽으로 정렬

\n

\"\"

\n

양쪽 정렬된 텍스트의 불규칙한 단어 간격

\n

양쪽 정렬이 왼쪽 또는 오른쪽 정렬된 텍스트보다 보기 좋다고 생각하는 사람들도 있지만, 이는 나쁜 관행으로 간주됩니다. text-align: justify는 같은 길이의 줄을 만들기 위해 단어 간격을 조정합니다. 이러한 불균일한 공백은 가독성을 해칠 수 있으며 매우 산만해질 수 있습니다. 필요한 경우 단어를 구분하는 것도 해결책이 될 수 있지만, CSS 하이픈은 잘 지원되지 않고 예상대로 작동하지 않을 수 있습니다.

\n

문단 너비 정의

\n

여러 출처에 따르면 디자이너들은 줄당 45에서 85자를 유지해야 한다고 합니다. 이상적인 문단 너비는 65자라고 여겨집니다.

\n

텍스트 블록의 너비를 정의할 때 ch 단위가 유용할 수 있습니다. 1ch는 숫자 0을 나타내는 문자의 너비와 동일합니다. 또한, font-family 또는 font-size가 변경되면 이에 따라 변경됩니다.

\n
p {\n  /* 최대 너비 65자 */\n  max-width: 65ch;\n}
\n

어떠한 종류의 반응형 타이포그래피 기술을 사용한다면, 매우 큰 화면에서 사이트를 테스트해야 합니다. 글꼴 크기에 제한이 없다면, 특정 뷰포트 크기에서 텍스트가 읽기 어려워질 수 있습니다. 제한을 설정하는 방법이나 반응형 타이포그래피에 익숙하지 않다면, Mike Riethmullers의 글 반응형 타이포그래피에 대한 정밀한 제어를 읽어보시기 바랍니다.

\n

🔗 가상 요소에 콘텐츠 신중하게 사용하기

\n

우리는 ::before::after라는 가상 요소를 사용하여 요소의 맨 처음이나 맨 끝에 CSS를 추가할 수 있습니다. 이것은 디자인 요소를 우리 컴포넌트에 추가하는 매우 일반적이고 편리한 방법을 제공하지만, content 속성을 사용하여 내용을 추가하는 것도 가능합니다. 관심사의 분리의 관점에서 보면, 우리는 이렇게 하지 않아야 합니다.

\n
h2 {\n  content: "DON'T DO THIS";\n}
\n

우리의 내용은 HTML 파일, 데이터베이스 또는 API에서 오는 것이지, CSS에서 오는 것이 아닙니다. 때때로 우리는 폰트 아이콘 또는 특수 문자와 같은 텍스트가 아닌 콘텐츠를 추가하기 위해 content 속성을 사용합니다. 그렇게 할 때, 일부 스크린 리더가 생성된 콘텐츠를 인식하고 설명한다는 것을 기억해야 합니다. 생성된 콘텐츠가 순전히 표현적인 경우 보조 기술에서 숨겨야 합니다. 예를 들어 aria-hidden을 사용할 수 있습니다.

\n
<span class="icon icon-key" aria-hidden="true"></span>
\n

🔗 화면만이 유일한 매체가 아니다

\n

우리가 디지털 시대에 살고 있음에도 불구하고, 사람들은 여전히 물건을 인쇄합니다. 당신의 페이지가 인쇄되거나 PDF로 저장될 때도 접근성이 좋고 사용하기 쉬워야 합니다. 우리가 해야 할 일은 CSS에 @media 블록을 추가하여 종이에 어울리지 않거나 의미가 없는 요소(네비게이션 또는 광고)들을 숨기거나 스타일을 조정하는 것입니다.

\n
@media print {\n  .header {\n    position: static;\n  }  nav {\n    display: none;\n  }\n}
\n

인쇄된 웹 페이지의 문제 중 하나는 링크가 완전히 쓸모없다는 것입니다. 왜냐하면 그것들이 어디로 이끄는지 알 수 없기 때문입니다. 다행히도 CSS는 속성의 값들을 드러내고 화면(이 경우에는 종이)에 표시하는 방법을 제공합니다.

\n
@media print {\n  a[href^="http"]:not([href*="mywebsite.com"])::after {\n    content: " (" attr(href) ")";\n  }\n}
\n

위의 코드는 href 속성이 있고 http로 시작하지만 mywebsite.com이 값에 포함되지 않은 모든 링크 옆에 href 속성의 값을 표시합니다.

\n

Firefox와 특히 Chrome은 인쇄용 스타일 시트를 테스트하고 디버깅하기 위한 도구를 제공합니다.

\n

더 깊이 파고들고 싶다면, 인쇄 스타일 작업을 위한 팁과 트릭들을 모아놓은 것이 있습니다.

\n

🔗 완전히 지원되지 않는 속성 값에 대한 대안

\n

가끔 우리는 특정 속성 값을 사용하고 싶지만, 일부 브라우저에서 지원하지 않기 때문에 사용할 수 없는 상황에 처합니다. 하지만 대안을 제공하는 한, 그것을 사용하는 것을 멈출 필요는 없습니다. 종종 쿼리나 다른 탐지 기능을 사용하지 않고도 할 수 있습니다.

\n

예를 들어, IE와 이전 버전의 Edge가 이해하지 못하는 vmax 단위를 사용하고 싶다고 가정해 봅시다.

\n
div {\n  width: 50vmax; /* IE와 Edge 이전 버전에서 작동하지 않음 */\n}
\n

대안을 제공하기 위해서, 덜 이상적이지만 브라우저가 이해할 수 있는 width 속성을 사용하면 됩니다. 예를 들어 width: 50vw처럼. 다음 줄에서 실제 원하는 값을 설정합니다.

\n
div {\n  width: 50vw;\n  width: 50vmax;\n}
\n

vmax를 이해하지 못하는 브라우저는 width: 50vw를 해석하고 width: 50vmax는 무시합니다. 반면에 vmax를 이해하는 브라우저는 먼저 width: 50vw를 해석한 다음 width: 50vmax를 해석합니다. vmax 선언이 vw 선언 다음에 오기 때문에, 유저는 vmax 로 설정한 버전이 적용 됩니다.

\n

🔗 콘텐츠를 숨기는 여러 가지 방법

\n

HTML의 제목들은 문서(document)의 개요를 잡는 데 매우 유용합니다. <h1>부터 <h6>까지의 제목을 사용함으로써, 브라우저와 다른 소프트웨어에게 문서가 어떻게 구성되어있고 각 부분들이 어떻게 연관되어 있는지 알려줍니다. 문서 개요를 가지는 것은 매우 중요합니다, SEO에 좋고 스크린 리더 사용자들이 사이트를 탐색하는 데 도움이 됩니다. 디자인에 제목이 없어도 제목이 있으면 좋을 것 같은 경우가 발생할 수 있습니다. 그것은 종종 디자인 자체가 구조를 전달할 때의 경우입니다. 이런 경우에는 마크업에서 제목을 단순히 제거하지 않고 시각적으로 숨깁니다. CSS가 있든 없든 문서의 구조가 명확해야 합니다.

\n

이것은 물론 단 하나의 예일 뿐이며, 폼에서 라벨을 시각적으로 숨기는 것은 또 다른 예입니다(UX 관점에서 라벨을 숨기는 것은 바람직하지 않습니다).

\n

CSS에서는 콘텐츠를 숨기는 여러 가지 방법이 있으며, 적절한 기술을 올바른 시나리오에 맞게 선택하는 것은 여러분에게 달려 있습니다.

\n

모든 사람으로부터 콘텐츠 숨기기

\n

hidden 속성을 사용하거나 visibility: hidden 또는 display: none을 설정함으로써 콘텐츠를 완전히 숨길 수 있습니다. 사용자는 볼 수 없으며 스크린 리더나 검색 엔진도 읽을 수 없습니다.

\n

시각적으로만 콘텐츠 숨기기

\n

시각적으로만 콘텐츠를 숨기는 것은 간단하지 않습니다. 스크린 리더는 여전히 접근 가능하게 해야 하며, 브라우저의 특이점을 다뤄야 하고 요소가 포커스될 때 무슨 일이 발생하는지 결정해야 합니다. 물론, 이미 다른 사람들이 이를 해냈고 해결책이 있습니다.

\n

제가 연구를 해본 결과, 많은 다양한 접근 방식이 있다는 것을 알게 되었습니다. 그래서 전문가들에게 의견을 물어보았고, 추천된 기술을 분석하여 무슨 일이 일어나는지 완전히 이해했습니다.

\n
.visually-hidden {\n  /* 일반적인 흐름에서 항목을 제거 */\n  position: absolute;\n  /* 잘못 발음되거나 뭉개지는 텍스트를 위한 해결책 */\n  white-space: nowrap;\n  /* 가능한 가장 작은 크기로 설정 (일부 화면 낭독기는 높이와 너비가 0인 요소를 무시함) */\n  width: 1px;\n  height: 1px;\n  /* 크기 조정 후 넘치는 콘텐츠 숨기기 */\n  overflow: hidden;\n  /* 요소의 크기를 변경할 수 있는 모든 속성 초기화 */\n  border: 0;\n  padding: 0;\n  /* 클리핑은 요소의 어떤 부분이 표시될지 정의함. */\n  /* 구식 clip 속성은 구형 브라우저를 위함 */\n  clip: rect(0 0 0 0);\n  /* 최신 브라우저를 위한 clip-path. inset(50%)는 콘텐츠를 사라지게 하는 내부 사각형을 정의함. */\n  clip-path: inset(50%);\n  /* 현재 아무도 정확히 왜 margin: -1px이 있는지 확실하지 않음. 게다가 이것이 문제를 일으킬 수 있음 (참조:https://github.com/h5bp/html5-boilerplate/issues/1985). */\n  margin: -1px;\n}
\n

이 클래스를 어딘가에 저장하고 시각적으로 콘텐츠를 숨기면서 보조 기술과 검색 엔진은 접근 가능하게 하고 싶을 때 사용하세요.

\n

스킵 링크

\n

앞의 클래스들은 스킵 링크로 사용하기에도 적합합니다. 스킵 링크는 초기에는 시각적으로 숨겨져 있지만 포커스될 때 보이는 링크입니다. 스크린 리더와 키보드 사용자들이 소개 콘텐츠를 건너뛰고 주요 콘텐츠로 바로 이동할 수 있도록 페이지의 첫 번째 항목 중 하나여야 합니다. 기본적으로 사용자를 페이지의 특정 부분으로 이동시키는 앵커 링크입니다.

\n

\"\"

\n

“Skip to content” 링크는 포커스될 때 보입니다

\n

코드 펜에서 직접 시도해보고, Tab을 눌러 건너뛰기 링크를 드러내보세요.

\n

의미론적으로 콘텐츠 숨기기

\n

때때로 시각적으로 콘텐츠를 표시하되 스크린 리더에서는 숨기는 것이 의미가 있습니다. 예를 들어 아이콘을 사용할 때 그렇습니다. 그런 경우에는 숨기고자 하는 요소에 aria-hidden 속성을 추가하고 true로 설정하세요.

\n
<button>\n  <span class="icon icon-hamburger" aria-hidden="true"></span>\n  <span class="text">Menu</span>\n</button>
\n

기타

\n

콘텐츠를 숨기는 다른 방법들도 있습니다. 예를 들어 음수 text-indent나 제로 font-size 또는 height 등입니다. 일부는 작동하지만 특정 주의사항이 있습니다. 자세한 내용은 webaim.org텍스트 숨기기 기술을 읽어보세요.

\n

🔗 나쁜 대비는 신뢰할 수 없다

\n

우리의 디자인은 가독성을 위해 텍스트와 배경 사이에 충분한 대비를 제공해야 합니다. 저시력자뿐만 아니라 시각 장애가 없는 사람들도 고대비에서 이익을 얻습니다. 맑은 날에 스마트폰을 밖에서 사용하는 것을 생각해 보세요.

\n

색상 대비란 무엇이며 왜 중요한가

\n

세계보건기구(World Health Organization)에 따르면 인구의 약 4%가 시각 장애가 있습니다. 남성의 7~12%와 여성의 1% 미만이 어떤 형태의 색상 시력 결함을 가지고 있습니다. 이러한 장애 중 많은 것이 대비에 대한 민감도를 감소시키고, 일부 경우에는 색상을 구별하는 능력까지 줄입니다.

\n

두 색상이 색상 휠의 다른 부분에서 올 때, 그 색상들은 대비를 이룹니다. 일반적으로 말해서, 두 색상의 차이가 클수록 대비가 높습니다. 웹 디자이너 및 개발자로서 우리에게는 단순히 대비 자체뿐만 아니라, 텍스트에 적용됐을 때 얼마나 잘 작동하는지가 중요합니다. 텍스트와 그 배경 사이의 대비는 적어도 중등도 저시력을 가진 사람들이 읽을 수 있을 정도로 높아야 합니다. 물론, 이 기준을 충족하는지 고민할 필요는 없습니다. 웹 접근성 이니셔티브(Web Accessibility Initiative, WAI)는 이를 측정하기 위한 비율을 정의했습니다.

\n

최소 대비 비율

\n

대비 비율은 특정 배경에서 특정 크기와 너비를 가진 텍스트의 대비가 얼마나 높은지를 알려줍니다. 비율은 1:1에서 21:1까지 다양할 수 있습니다. 비교된 두 색상이 동일하면 1:1이고, 검정과 흰색이 대립되는 경우 21:1입니다.

\n

\"\"

\n

#777777 색상의 텍스트가 #DDDDDD 배경에서 3.3:1 비율을 가집니다. (출처: 대비 비율)

\n

웹 콘텐츠 접근성 지침(WCAG) 2.0에 따르면, 배경과 그 텍스트(또는 텍스트 이미지) 사이에 최소 4.5:1의 대비 비율이 존재해야 합니다. 이는 24px 미만(굵지 않은 경우) 및 19px 미만(굵은 경우)인 텍스트에 적용됩니다. 더 큰 텍스트의 경우 3:1 비율이면 충분합니다. 이것들은 레벨 AA 기준을 충족하기 위한 최소 수치입니다. 레벨 AAA를 통과하려면 일반 텍스트의 최소 비율은 7:1이고 굵은 텍스트는 4.5:1입니다. 규정 준수를 위한 필수 사항은 아니지만, 우리가 아이콘을 사용한다면 텍스트 대비 규정을 충족하는 아이콘을 사용해야 합니다.

\n

저는 제 친구 Daniel에게 비율에 대해 말했고, 우리가 현재 작업 중인 프로젝트에서 그것을 올바르게 가져가는 것이 중요하다고 말했습니다. 다양한 조합을 시도한 후, 그는 이것이 생각했던 것보다 어렵다고 전화로 말했습니다. 문제는 충분히 시각적으로 매력적인 조합이 없는 것이 아니라, 지난 몇 년 동안 디자이너들이 저대비 조합을 사용하는 데 익숙해졌다는 것입니다. 작은 에이전시뿐만 아니라 애플이나 구글과 같은 큰 회사들도 이 불리한 디자인 추세를 따르고 있습니다.

\n
\n

나이가 확실히 내 시력에 영향을 미쳤지만, 나는 디자인 트렌드로 고통받고 있다.

\n
\n

Kevin Marks

\n

대비 비율을 계산하기 위한 공식이 있지만, 오래된 계산기를 꺼내서 계산할 필요는 없습니다. 도구들이 있습니다.

\n

대비 비율 측정

\n

Chrome Canary에서는 개발자 도구에서 직접 대비 비율을 표시할 수 있습니다. Remy Sharp가 블로그 글에서 이를 공유합니다.

\n

\"\"

\n

Chrome 개발자 도구에서의 대비 비율.

\n

색상 대비와 일반적인 접근성을 테스트하기 위한 많은 도구들이 있습니다. 다음 목록은 광범위하지는 않지만, 제가 선호하는 도구들의 작은 모음집 입니다.

\n

온라인

\n\n

브라우저에서 빠르고 쉬운 대비 체커

\n\n

브라우저에서 좀 더 많은 옵션을 가진 대비 체커

\n\n

브라우저 도구로 대비와 더 많은 것들을 체크

\n\n

자동 대비 체크가 있는 색상 선택기

\n

브라우저 확장 프로그램과 개발자 도구

\n\n

Chrome 60은 Lighthouse를 기반으로 한 새로운 감사 패널과 함께 출시되었습니다. 접근성 점수를 부여하고 문제를 나열합니다.

\n\n

대비, 문서 개요 등을 테스트하기 위한 훌륭한 브라우저 확장 프로그램.

\n\n

aXe Chrome 확장 프로그램을 사용하여 웹 사이트에서 접근성 결함을 자동으로 찾는 도구.

\n

기타

\n\n

“WCAG에 대한 두 레이어의 색상 대비를 계산하는 Sketch 플러그인.

\n\n

고대비의 경험

\n

고대비의 색상을 사용하는 것도 훌륭하지만, 저시력을 가진 사람들은 여전히 웹사이트에서 사용하는 색상을 변경하고 싶어할 수 있습니다. 사용자의 요구는 매우 다양하며 그에 따라 색상 변경 방법도 다양합니다. 이 사실은 어느 정도 예측 불가능성을 내포하고 있으며, 우리의 페이지들이 항상 완전한 접근성을 보장하기 어렵게 만듭니다. 그래서 우리는 단지, 대비 수준 AA 또는 AAA 기준을 충족하는 것에만 의존해서는 안 되며, 웹사이트를 철저히 테스트하고 대비가 높은 대안을 제공하는 것도 고려해야 합니다.

\n

윈도우에서의 고대비 모드

\n

윈도우에서는 설정에서 고대비 옵션을 사용할 수 있습니다. 사용자는 자신만의 색상 설정을 정의하거나 사전 정의된 테마를 선택할 수 있습니다.

\n

\"\"

\n

윈도우에서의 고대비 설정

\n

간단한 로그인 폼을 만들었고(첫 번째 스크린샷 중 하나. https://dribbble.com/shots/1687064-Simple-Login-Form으로 부터 영감을 받음.) 고대비를 가진 다양한 테마로 테스트해 보았습니다.

\n

\"\"

\n

고대비 설정에서의 다양한 로그인 폼

\n

Anika Henke는 사용자들이 웹사이트에서 색상을 어떻게 변경하는지에 대해 썼습니다. 그녀는 폼을 테스트하던 중 입력 필드가 보이지 않게 되었고 버튼이 인식되지 않게 되었다는 것을 발견했습니다. 위 스크린샷에서도 같은 일이 발생하는 것을 볼 수 있습니다. 대체 텍스트가 없었다면, 사용자들은 두 개의 입력 필드가 있다는 것을 알지 못했을 것입니다. 인풋과 버튼에 기본 테두리를 추가하는 것은 빠른 해결책이었습니다(브라우저 간 테스트되지 않음).

\n

\"\"

\n

고대비 설정에서 인풋과 버튼에 테두리가 있는 개선된 로그인 폼

\n

미디어 쿼리를 사용하여 고대비 모드가 활성화되었는지 감지하고 특정 스타일을 제공할 수 있습니다.

\n
/* 고대비 모드 활성화 */\n@media (-ms-high-contrast:active) {\n}/* 특정 검정-백색 테마의 고대비 모드 */\n@media (-ms-high-contrast:black-on-white) {\n}/* 특정 검정-백색 테마의 고대비 모드 */\n@media (-ms-high-contrast:white-on-black) {\n}
\n

Patrick H. Lauke 는 윈도우 고대비 모드: -ms-high-contrast의 제한된 유용성에서 이 미디어 기능에 대한 그의 생각과 우려를 공유했습니다. 이에 응답으로 Greg Whitworth는 다음과 같이 지적했습니다. “이 기능의 유일한 목적은 대비 민감도를 가진 사용자들에게 더 나은 경험을 제공하는 것입니다. 그러므로, 특정 색상이 무엇인지에 대해 반드시 신경 쓸 필요는 없습니다. 어느 정도까지는, 여러분의 사이트가 어떻게 보이는지보다 어떻게 고대비에서 기능하는지에 대해 신경 써야 합니다.”

\n

높은 대비 크롬 확장 프로그램

\n

구글 크롬을 위한 고대비 확장 프로그램도 있어, 사용자들이 텍스트를 읽기 쉽게 만드는 여러 고대비 색상 필터로 웹을 탐색할 수 있습니다.

\n

고대비의 대안

\n

디자인의 일부분이 충분한 대비를 가지고 있지 않더라도, Alternate Version 조항을 사용하여 WCAG 기준을 충족할 수 있습니다. 이에 따르면, 사용자에게 페이지의 고대비 버전으로 연결하는 링크나, 페이지의 모든 측면이 준수하도록 페이지를 변경할 수 있는 페이지 상의 컨트롤을 제공해야 합니다.

\n

이 대안에 대한 몇 가지 기준이 있습니다.

\n\n

NoCoffee로 테스트하기

\n

\"\"

\n

NoCoffee는 저시력, 색상 결핍 및 차단된 시각 영역을 시뮬레이션합니다

\n

기준을 충족하는 것과 실제 사람을 대상으로 테스트하는 것은 별개의 문제입니다. 모든 사람이 전문적인 테스트 수단을 가지고 있는 것은 아닙니다. 다행히도, NoCoffee는 저시력, 색상 결핍 및 차단된 시각 영역을 쉽게 시뮬레이션할 수 있는 방법을 제공합니다. 이는 경미한~극심한 시력 문제를 가진 사람들이 직면하는 문제를 이해하는 데 도움이 될 수 있습니다.

\n

🔗 색상이 정보의 유일한 단서가 되어서는 안 된다

\n

앞서 언급했듯이, 많은 남성들이 시력 결함을 가지고 있습니다. 유형도 다양합니다. 가장 흔한 유형 중 하나인 중색 이상(Deuteranomaly)은 빨강과 녹색을 구분하기 어렵게 만듭니다. 색상 시력 결함이 있는 사람은 인터페이스를 사용할 수 없게 될 수 있으므로 색상만을 시각적 단서로 사용하는 것을 피해야 합니다.

\n

이전 예시에서 보여준 폼의 입력 필드에 성공 및 오류 상태를 나타내는 테두리를 추가했습니다. 다음 스크린샷은 색상만으로 사용자에게 충분한 피드백을 주지 못한다는 것을 보여줍니다. 테두리 색상이 전혀 보이지 않거나 잘못 보이는 경우가 있습니다.

\n

\"\"

\n

고대비 모드에서 색상만으로 폼의 성공과 실패를 구별하는 것은 효과적이지 않습니다.

\n

간단한 아이콘을 추가하면 접근성과 사용자 경험을 개선하는 데 도움이 될 수 있습니다.

\n

링크도 비슷한 예시 중 하나입니다. 링크는 색상만으로 일반 텍스트와 구별되어서는 안 됩니다. 링크에 밑줄을 유지하는 것이 좋습니다.

\n

🔗 순서에 신경 쓰기

\n

아이템들을 배치 순서를 바꾸는 방법은 많이 있습니다. 예를 들어, Flexbox에는 orderflex-direction이 있고, Grid에는 order, flex-auto-flow 및 명시적 배치가 있습니다. 이러한 속성들은 매우 유용하지만, 콘텐츠의 DOM 순서와 시각적 표현 사이의 연결이 끊어질 수 있습니다.

\n

다음 예시에서는 여러 그리드 속성을 사용하여 배치된 갤러리의 이미지를 볼 수 있습니다.

\n\n

처음에는 문제가 없어 보이지만, 키보드를 사용하여 이미지에서 이미지로 이동할 때 순서를 전혀 예측할 수 없다는 것을 알 수 있습니다. Tab 키를 눌렀을 때 다음에 어떤 이미지가 강조될지 알 수가 없습니다. 여기에 포커스 스타일이 없으면 최악의 상황이 될 수 있습니다.

\n\n

예측 불가능하거나 잘못된 순서는 키보드 사용자뿐만 아니라 스크린 리더 사용자에게도 문제가 됩니다. 스크린 리더는 DOM 순서대로 콘텐츠를 표시하므로 소프트웨어는 CSS 순서에 영향을 받지 않지만 사용자는 영향을 받습니다. 스크린 리더 사용자는 콘텐츠의 시각적 표현에 신경 쓰지 않는다고 생각할 수 있지만, 모든 스크린 리더의 사용자가 시각 장애가 있는 것은 아닙니다. 일부는 저시력이나 학습 장애가 있어 화면에 표시되는 내용을 보완하기 위해 스크린 리더를 사용합니다.

\n

이 순서 문제는 플렉스나 그리드 아이템뿐만 아니라 모든 종류의 위치 지정에도 적용됩니다. 스타일 없이도 의미가 있는 방식으로 콘텐츠를 정렬하는 것이 중요하며, 디자인에서의 순서와 일치하는지 확인해야 합니다. 일치하지 않으면 디자인을 다시 생각해야 할 수 있습니다. CSS에서 올바르게 위치시키지 못한다고 해서 마크업에서 요소들을 임의로 재배열하지 마세요.

\n

Rob Dodson’s의 콘텐츠 재배열이 접근성에 영향을 미치는가?와 Adrian Roselli의 코드의 순서가 중요하다 글을 참고해 더 자세한 내용을 알아보세요.

\n

🔗 중요한 것에 집중하기: focus

\n

저는 이미 키보드 탐색 기초와 포커스 가능한 요소들에 대해 접근성을 고려하여 자바스크립트 작성하기라는 글을 썼습니다. 이 주제가 처음이시라면, 그 글을 빠르게 읽고 오세요.

\n

키보드를 사용하여 웹사이트를 탐색할 수 있도록 하는 것은 중요합니다. 많은 사용자들이 웹 서핑을 할 때 키보드에 의존합니다. 그들 중에는 운동 장애가 있는 사람들, 시각 장애가 있는 사람들, 손이 없거나 어떤 이유로든 마우스나 트랙패드를 사용할 수 없는 사람들이 있습니다.

\n

CSS를 사용해서 포커스 가능한 요소들에 스타일을 적용할 수 있는 몇 가지 방법이 있습니다.

\n

포커스된 아이템 선택하기**

\n

:focus 의사 클래스를 사용하여 포커스 가능한 아이템들이 포커스 되었을때 스타일을 적용할 수 있습니다.

\n
a:focus {\n  background-color: #000000;\n  color: #FFFFFF;\n}
\n

기본 포커스 스타일은 브라우저마다 일관성이 없으며 보기 좋지 않은 경우가 많으며, 때로는 디자인과 잘 어울리지 않습니다. 사용자 경험을 개선하고 디자인에 맞는 맞춤 포커스 스타일을 제공하는 것이 좋습니다.

\n

하지만 무엇을 하든, 대체 스타일을 제공하지 않고 기본 윤곽선(점선 윤곽, 파란색 또는 주황색 반지)만 제거하지 마세요. 주로 키보드로 탐색하는 사용자들은 포커스 위치를 알 수 없다면 사이트를 사용할 수 없게 됩니다.

\n

\"\"

\n

대안 없이 기본 포커스 스타일을 제거하지 마세요 (출처: outlinenone.com)

\n

이것은 단순한 팁이 아니라 레벨 AA 기준 기준입니다.

\n

키보드와 마우스 사용자 구분하기

\n

이미 언급했듯이, 디자이너들이 좌절하는 것 중 하나는 브라우저 간 포커스 스타일의 일관성이 부족하다는 것입니다. 또한 마우스를 사용할 때도 일부 포커스 가능한 요소들에서 포커스 스타일이 보이는 점도 문제입니다. 때로는 마우스 사용자에게는 포커스 스타일을 보여줄 필요가 없으며, 심지어 방해가 되거나 미학적으로 불쾌할 수 있습니다.

\n

\"\"

\n

컨텐츠 영역이 클릭되었을 때 크롬에서 파란색 윤곽선이 보이는 맞춤 탭 컴포넌트 (출처: frend.co)

\n

웹 페이지의 특정 요소에 포커스가 가 있을 때 outline 속성을 제거해서는 안됩니다. 왜냐하면 컴포넌트가 더 이상 키보드 사용자에게 접근 불가능하기 때문입니다. 우리에게 필요한 것은 키보드와 마우스 사용을 구별하는 방법입니다. 이것은 CSS Level 4 선택자 명세의 일부인 :focus-ring 가상 클래스를 사용하여 가능 합니다. ”:focus-ring 가상 클래스는 요소가 :focus 가상 클래스와 일치하고, 사용자 에이전트(브라우저)가 휴리스틱(규칙이나 패턴)을 통해 요소에 특별히 표시되어야 함을 결정할 때 (일반적으로 ‘focus-ring’을 통해) 적용됩니다.” (출처: CSS 선택자 레벨 4 초안)

\n
/* 기본 윤곽선 제거 */\n:focus {\n  outline: none;\n}\n\n/* 윤곽선이 보여야 할 때만 추가 */\n:focus-ring {\n  outline: 2px solid blue;\n}
\n

아쉽게도 현재 어떤 브라우저도 :focus-ring의 표준 구현을 지원하지 않습니다. (Firefox는 -moz-focus-ring을 지원함), 하지만 적절할 때 .focus-ring 클래스를 추가하는 경량 폴리필이 있습니다.

\n
/* 자바스크립트가 활성화되어 작동하고, .focus-ring 클래스가 없는 \n모든 포커스 가능한 요소를 선택하여 윤곽선을 제거합니다 */\n.js-focus-ring :focus:not(.focus-ring) {\n    outline-width: 0;\n}
\n

자세한 내용은 Rob Dodson의 a11ycasts 에피소드, Focus Ring!을 시청하세요.

\n

포커스된 자식이 있는 요소에 대한 스타일링

\n

:focus-within는 상대적으로 새로운 가상 클래스로, 이미 대부분의 주요 브라우저에서 지원되고 있습니다. 이 클래스를 사용하면 현재 포커스된 자식 요소를 가진 요소를 선택할 수 있습니다.

\n

\"\"

\n

자식 항목 중 하나가 포커스되면 그림자가 표시되는 예시입니다.

\n
form:focus-within {\n  box-shadow: 0 0 4px 6px rgba(80,88,156,0.2);\n}
\n

이 기능을 CodePen에서 확인할 수 있습니다.

\n

포커스에 대한 자세한 내용은 YouTube에서 포커스란 무엇인가요? 영상을 참조하세요.

\n

🔗 그리드와 평평한 문서 구조

\n

새로운 사이트를 만들 때, 우리는 보통 HTML 작성부터 시작합니다. 적절한 마크업을 선택하고 요소들을 논리적 순서에 맞게 배치합니다. 문서가 올바르게 작성되어 있고 잘 구조화되어 있으며 순서가 의미 있게 되면 CSS를 추가합니다. CSS Grid Layout이 나오기 전에는, 특히 DOM 순서와 디자인 순서가 일치하지 않은 경우에 레이아웃을 만드는 것이 매우 까다로웠습니다. float, position 그리고 때때로 Flexbox조차도 어떤 상황에서는 충분히 유연하지 않았고, 우리는 DOM 순서를 변경하고 싶은 유혹에 빠지곤 했습니다. Grid의 명시적 배치와 해당 영역 덕분에 아이템을 위치시키는 데 필요한 모든 유연성을 갖게 되었습니다. 이것은 훌륭하지만, Grid는 문서 구조를 해칠 수 있는 새로운 유혹을 도입합니다.

\n

다음과 같은 디자인을 가지고 있고, 그 아이템들에 h2ul을 사용한다고 가정해 봅시다. 왜냐하면 그것이 당신에게 가장 의미가 있기 때문입니다.

\n

\"\"

\n

제목과 목록이 있는 레이아웃

\n
<div class="wrapper">\n  <h2>Heading</h2>\n  <ul>\n    <li><a href="#">Element 1</a></li>\n    <li><a href="#">Element 2</a></li>\n    <li><a href="#">Element 3</a></li>\n    <li><a href="#">Element 4</a></li>\n    <li><a href="#">Element 5</a></li>\n    <li><a href="#">Element 6</a></li>\n  </ul>\n</div>
\n

이러한 요소들을 열에 넣고 <h2>를 위치시키는 것은 꽤 쉽습니다… 또는 적어도 그렇게 보입니다.

\n
.wrapper {\n  display: grid;\n  grid-template-columns: 120px repeat(2, 1fr);\n  grid-gap: 20px;\n}h2 {\n  grid-column: 2 / -1;\n}
\n

\"\"

\n

제목과 목록이 있는 레이아웃. 그리드 컨테이너의 직접적인 자식만 그리드에 배치됩니다.

\n

하지만 예상과는 달리 보입니다. 문제는 그리드 컨테이너의 직접적인 자식만이 그리드에 배치된다는 것인데, 이 경우에는 <h2><ul>이 해당됩니다. 하지만 여러분은 <li>들을 그리드 아이템으로 만들고 싶습니다. 이 문제에 대한 최악의 해결책은 구조를 단순화시켜 <ul>을 제거하고 <li><div>로 변환하여 그리드 컨테이너의 직접적인 자식으로 만드는 것입니다.

\n

가장 좋은 해결책은 <ul>display 속성을 subgrid로 설정하는 것이지만, 불행히도 subgrid는 명세의 레벨 1에 포함되지 않았고 우리는 그것이 출시될 때까지 더 기다려야 합니다.

\n

<ul>display: contents를 사용할 수 있지만, 현재 Firefox만이 이를 지원합니다. display: contents는 요소의 자식들이 마치 요소의 부모의 직접적인 자식인 것처럼 보이게 만듭니다.

\n

결국, **<ul>**에 다른 그리드를 정의해야 합니다. 이것은 이상적이지 않지만, 문서의 구조를 단순화시키고 의미를 해치는 것보다는 낫습니다. 이것은 매우 기본적인 예제이며 목록이 전체 그리드를 차지하기 때문에 부모 그리드에서 일부 값을 상속받을 수 있습니다.

\n
ul {\n  /* 전체 그리드를 차지함 */\n  grid-column: 1 / -1;  /* 다른 그리드를 생성하고 부모 그리드에서 값을 상속받음 */\n  display: inherit;\n  grid-template-columns: inherit;\n  grid-gap: inherit;  /* display: contents를 이해하는 브라우저를 위해 display 덮어쓰기 */\n  display: contents;\n}
\n

CodePen에서 두 가지 솔루션을 볼 수 있습니다.

\n

결론

\n

이 글에서 꽤 많은 내용을 다루고 있지만 CSS와 접근성에 대해 알아야 할 모든 것을 다루지는 않습니다. 그러나 이것은 단순한 출발점 그 이상입니다. DOM 및 포커스 순서를 올바르게 설정하고 고대비에 신경 쓰고 일반적으로 접근성을 고려하여 디자인 하는 것만으로도 이미 훌륭한 작업을 하고 있는 것입니다. 새로운 페이지나 사이트를 만들 때마다 접근성을 약간만 더 고려한다면 웹을 더 나은 곳으로 만들 수 있습니다.

\n
\n

제약사항을 고려하여 디자인하는 것은 단순히 잘 디자인하는 것이다.

\n
\n

Aaron Gustafson

\n

이 글을 즐겁게 읽으셨고 새로운 것을 배우셨기를 바랍니다. 질문이나 피드백이 있으시면 댓글을 남겨주시거나 트위터를 통해 연락해 주시기 바랍니다.

\n

이 글을 작성하는 데 도움을 주신 멘토 Aaron Gustafson에게 감사드립니다.

\n

더 많은 접근성 팁

\n

이 글은 네 부분 시리즈 중 세 번째입니다. 마지막 글은 준비 중이며 곧 공개될 예정입니다.

\n
    \n
  1. 접근성을 고려한 HTML 작성하기
  2. \n
  3. 접근성을 고려한 JavaScript 작성하기
  4. \n
  5. 접근성을 고려한 CSS 작성하기
  6. \n
  7. 다음 글: 접근성을 고려한 디자인 및 개발 방법 배우기
  8. \n
\n

독자 여러분의 독서와 이 글을 좋아하시고 공유해 주시면 감사하겠습니다.

\n

제가 작성한 다른 글도 확인해보실 수 있습니다.

\n

Progressively Enhancing CSS Layout: From Floats To Flexbox To Grid

\n

The Difference Between Explicit and Implicit Grids

\n

추가적인 읽을 거리와 자료들

\n

가독성 있는 텍스트 작성법

\n\n

가상 요소에 콘텐츠 신중하게 사용하기

\n\n

화면만이 유일한 매체가 아니다

\n\n

완전히 지원되지 않는 속성 값에 대한 대안

\n\n

콘텐츠를 숨기는 여러 가지 방법

\n\n

나쁜 대비는 신뢰할 수 없다

\n\n

색상이 정보의 유일한 단서가 되어서는 안 된다

\n\n

순서에 신경 쓰기

\n\n

중요한 것에 집중하기: focus

\n\n

그리드와 평평한 문서 구조

\n\n","frontmatter":{"date":"February 18, 2024","title":"(번역) 접근성을 고려하여 CSS 작성하기","categories":"translate a11y","author":"soobing"},"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}},"prev":{"id":"e5b56e8b-108e-5455-8e4a-ebbfe2c18f09","html":"

프론트 개발을 하다보면 웹 페이지의 다양한 요소들을 직접 제어해야 하는 경우가 있습니다. 이 글에서는 CSSOM의 네 가지 주요 좌표 체계인 Offset, Page, Viewport, Screen에 대해 자세히 알아보고, 위치와 크기 관련 API는 어떤것이 있는지 살펴볼 예정입니다. 이 글을 통해 웹 페이지 내에서 요소의 위치와 크기를 정밀하게 제어하는 데 도움이 되길 바랍니다.

\n

표준 CSSOM 좌표 체계

\n

보통 클라이언트 개발에서 좌표의 원점은 왼쪽 하단이 아닌, 왼쪽 상단 입니다.

\n

CSS 객체 모델에서는 Offset, Page, Viewport, Screen 네 가지 표준 좌표 체계가 있습니다.

\n

\n \n \n

\n

출처: MDN Coordinate_systems

\n

Offset

\n\n

Page

\n\n

Viewport

\n\n

Screen

\n\n

참고: 상대적 위치 vs 절대적 위치

\n\n

브라우저 제공 API

\n
\n

💻 window 제공 API, 🌱 Element 제공 API

\n
\n

1. 브라우저 창 크기와 뷰포트

\n\n

2. 요소의 크기와 경계

\n\n

3. 스크롤 관련 정보

\n\n

4. 마우스 위치

\n\n

5. 스타일 정보

\n\n","frontmatter":{"date":"March 03, 2024","title":"브라우저 위치 및 크기 관련 API들","categories":"browser","author":"soobing"},"fields":{"slug":"/browser/browser-coordinate-size-api/"}},"site":{"siteMetadata":{"siteUrl":"https://soobing.github.io","comments":{"utterances":{"repo":"soobing/soobing.github.io"}}}}},"pageContext":{"slug":"/infra/docker-command/","nextSlug":"/a11y/writing-css-with-accessibility-in-mind/","prevSlug":"/browser/browser-coordinate-size-api/"}}, "staticQueryHashes": ["1073350324","2938748437"]} \ No newline at end of file diff --git a/page-data/posts/All/page-data.json b/page-data/posts/All/page-data.json index 4ed99a67..632168e5 100644 --- a/page-data/posts/All/page-data.json +++ b/page-data/posts/All/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-category-template-js", "path": "/posts/All", - "result": {"pageContext":{"currentCategory":"All","categories":["All","framework","react","next","translate","typescript","browser","a11y","infra","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[]}}, + "result": {"pageContext":{"currentCategory":"All","categories":["All","framework","react","next","translate","typescript","browser","infra","a11y","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[]}}, "staticQueryHashes": ["1073350324","1956554647","2938748437"]} \ No newline at end of file diff --git a/page-data/posts/a11y/page-data.json b/page-data/posts/a11y/page-data.json index 6d166481..ddb6aa11 100644 --- a/page-data/posts/a11y/page-data.json +++ b/page-data/posts/a11y/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-category-template-js", "path": "/posts/a11y", - "result": {"pageContext":{"currentCategory":"a11y","categories":["All","framework","react","next","translate","typescript","browser","a11y","infra","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"49246cb3-c4b8-5aeb-a4c5-a1b737e89b51","excerpt":"원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939 CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다. 이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다. 읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다. 약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리…","fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"},"frontmatter":{"categories":"translate a11y","title":"(번역) 접근성을 고려하여 CSS 작성하기","date":"February 18, 2024","draft":false}},"next":{"fields":{"slug":"/infra/docker-command/"}},"previous":{"fields":{"slug":"/browser/browser-coordinate-size-api/"}}}]}}, + "result": {"pageContext":{"currentCategory":"a11y","categories":["All","framework","react","next","translate","typescript","browser","infra","a11y","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"49246cb3-c4b8-5aeb-a4c5-a1b737e89b51","excerpt":"원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939 CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다. 이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다. 읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다. 약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리…","fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"},"frontmatter":{"categories":"translate a11y","title":"(번역) 접근성을 고려하여 CSS 작성하기","date":"February 18, 2024","draft":false}},"next":{"fields":{"slug":"/react/storybook-react-server-components/"}},"previous":{"fields":{"slug":"/infra/docker-command/"}}}]}}, "staticQueryHashes": ["1073350324","1956554647","2938748437"]} \ No newline at end of file diff --git a/page-data/posts/browser/page-data.json b/page-data/posts/browser/page-data.json index 5349120b..f22e11c8 100644 --- a/page-data/posts/browser/page-data.json +++ b/page-data/posts/browser/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-category-template-js", "path": "/posts/browser", - "result": {"pageContext":{"currentCategory":"browser","categories":["All","framework","react","next","translate","typescript","browser","a11y","infra","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"e5b56e8b-108e-5455-8e4a-ebbfe2c18f09","excerpt":"프론트 개발을 하다보면 웹 페이지의 다양한 요소들을 직접 제어해야 하는 경우가 있습니다. 이 글에서는 CSSOM의 네 가지 주요 좌표 체계인 Offset, Page, Viewport, Screen에 대해 자세히 알아보고, 위치와 크기 관련 API는 어떤것이 있는지 살펴볼 예정입니다. 이 글을 통해 웹 페이지 내에서 요소의 위치와 크기를 정밀하게 제어하는 데 도움이 되길 바랍니다. 표준 CSSOM 좌표 체계 보통 클라이언트 개발에서 좌표의 원점은 왼쪽 하단이 아닌, 왼쪽 상단 입니다. CSS 객체 모델에서는 Offset, Page, Viewport, Screen 네 가지 표준 좌표 체계가 있습니다. 출처: MDN Coordinate_systems Offset 정의: 오프셋 좌표 체계는 특정 요소의 상대적 위치를 나타내는 데 사용됩니다. 이는 요소의 왼쪽 상단 모서리를 시작점(원점)으로 합니다. 특징: 이 좌표 체계는 요소의 부모 요소를 기준으로 한 상대적 위치를 제공합니다. 마…","fields":{"slug":"/browser/browser-coordinate-size-api/"},"frontmatter":{"categories":"browser","title":"브라우저 위치 및 크기 관련 API들","date":"March 03, 2024","draft":false}},"next":{"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}},"previous":{"fields":{"slug":"/react/html-node-streaming/"}}},{"node":{"id":"47a3c1ed-8f39-56be-81c1-c0cd470b4c41","excerpt":"문제상황 dropdown 컴포넌트를 만들었는데, focus가 다른곳으로 이동하면 펼쳐져있던 dropdown이 접혀져야 해서 blur 이벤트 발생시 접히도록 했다. 그랬더니 펼쳐져 있는 항목 중 하나를 click 했을때, 위에서 설정한 blur 이벤트가 먼저 발생하면서 click 이벤트가 호출되지 않는 이슈가 문제였다. dropdown_비정상동작.gif 문제의 코드 TL;DR Touch, mouse 이벤트 순서 touchstart touchmove touchend mouseover mousemove mousedown mouseup click 해결방법 click시 처리해줘야 하는 이벤트 핸들러를 click 이 아닌 mousedown 에서 처리해준다. dropdown_정상동작.gif Dropdown CodeSandbox Touch and mouse 이벤트 그러면 지금부터는 web.dev 문서를 살펴보면서 알게된 touch 이벤트의 도입 배경과 touch, mouse 이벤트를 함께 다루기…","fields":{"slug":"/browser/touch-mouse-event/"},"frontmatter":{"categories":"feature browser","title":"Touch, Mouse 이벤트 함께 다루기 (feat. dropdown)","date":"September 18, 2022","draft":false}},"next":{"fields":{"slug":"/infra/jenkins-bitbucket/"}},"previous":{"fields":{"slug":"/react/error-handle/"}}}]}}, + "result": {"pageContext":{"currentCategory":"browser","categories":["All","framework","react","next","translate","typescript","browser","infra","a11y","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"e5b56e8b-108e-5455-8e4a-ebbfe2c18f09","excerpt":"프론트 개발을 하다보면 웹 페이지의 다양한 요소들을 직접 제어해야 하는 경우가 있습니다. 이 글에서는 CSSOM의 네 가지 주요 좌표 체계인 Offset, Page, Viewport, Screen에 대해 자세히 알아보고, 위치와 크기 관련 API는 어떤것이 있는지 살펴볼 예정입니다. 이 글을 통해 웹 페이지 내에서 요소의 위치와 크기를 정밀하게 제어하는 데 도움이 되길 바랍니다. 표준 CSSOM 좌표 체계 보통 클라이언트 개발에서 좌표의 원점은 왼쪽 하단이 아닌, 왼쪽 상단 입니다. CSS 객체 모델에서는 Offset, Page, Viewport, Screen 네 가지 표준 좌표 체계가 있습니다. 출처: MDN Coordinate_systems Offset 정의: 오프셋 좌표 체계는 특정 요소의 상대적 위치를 나타내는 데 사용됩니다. 이는 요소의 왼쪽 상단 모서리를 시작점(원점)으로 합니다. 특징: 이 좌표 체계는 요소의 부모 요소를 기준으로 한 상대적 위치를 제공합니다. 마…","fields":{"slug":"/browser/browser-coordinate-size-api/"},"frontmatter":{"categories":"browser","title":"브라우저 위치 및 크기 관련 API들","date":"March 03, 2024","draft":false}},"next":{"fields":{"slug":"/infra/docker-command/"}},"previous":{"fields":{"slug":"/react/html-node-streaming/"}}},{"node":{"id":"47a3c1ed-8f39-56be-81c1-c0cd470b4c41","excerpt":"문제상황 dropdown 컴포넌트를 만들었는데, focus가 다른곳으로 이동하면 펼쳐져있던 dropdown이 접혀져야 해서 blur 이벤트 발생시 접히도록 했다. 그랬더니 펼쳐져 있는 항목 중 하나를 click 했을때, 위에서 설정한 blur 이벤트가 먼저 발생하면서 click 이벤트가 호출되지 않는 이슈가 문제였다. dropdown_비정상동작.gif 문제의 코드 TL;DR Touch, mouse 이벤트 순서 touchstart touchmove touchend mouseover mousemove mousedown mouseup click 해결방법 click시 처리해줘야 하는 이벤트 핸들러를 click 이 아닌 mousedown 에서 처리해준다. dropdown_정상동작.gif Dropdown CodeSandbox Touch and mouse 이벤트 그러면 지금부터는 web.dev 문서를 살펴보면서 알게된 touch 이벤트의 도입 배경과 touch, mouse 이벤트를 함께 다루기…","fields":{"slug":"/browser/touch-mouse-event/"},"frontmatter":{"categories":"feature browser","title":"Touch, Mouse 이벤트 함께 다루기 (feat. dropdown)","date":"September 18, 2022","draft":false}},"next":{"fields":{"slug":"/infra/jenkins-bitbucket/"}},"previous":{"fields":{"slug":"/react/error-handle/"}}}]}}, "staticQueryHashes": ["1073350324","1956554647","2938748437"]} \ No newline at end of file diff --git a/page-data/posts/cs/page-data.json b/page-data/posts/cs/page-data.json index 7c8dc9eb..7b0943dc 100644 --- a/page-data/posts/cs/page-data.json +++ b/page-data/posts/cs/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-category-template-js", "path": "/posts/cs", - "result": {"pageContext":{"currentCategory":"cs","categories":["All","framework","react","next","translate","typescript","browser","a11y","infra","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"44fd3704-304a-5b2a-8702-af196e247d28","excerpt":"원글: https://github.com/kamranahmedse/design-patterns-for-humans/blob/master/readme.md 역자주: 이 글은 Kamran Ahmed의 글이며, PHP-7 기준으로 작성된 예제 코드를 타입스크립트로 변경하였습니다. logo 🎉 디자인 패턴에 대한 초간단 설명입니다! 🎉 누구든지 혼란스럽게 만들 수 있는 주제입니다. 이 글에서는 최대한 간단한 방식으로 설명함으로써 당신의 기억 속 (그리고 아마도 제 기억)에 각인시키기 위해 노력할 것입니다. 저의 다른 프로젝트도 확인하고 트위터에서 “안녕”이라고 인사해 보세요. 생성 패턴 구조 패턴 행동 패턴 심플 팩토리(Simple Factory) 어댑터(Adapter) 책임 연쇄(Chain of Responsibility) 팩토리 메서드(Factory Method) 브릿지(Bridge) 명령(Command) 추상 팩토리(Abstract Factory) 컴포지트(Composite) 반복자…","fields":{"slug":"/cs/design-patterns-for-humans/"},"frontmatter":{"categories":"translate cs","title":"(번역) 우리들을 위한 디자인 패턴","date":"August 27, 2023","draft":false}},"next":{"fields":{"slug":"/javascript/garbage-collection/"}},"previous":{"fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"}}},{"node":{"id":"872218ff-8507-5910-8001-bc52d72fca27","excerpt":"원글: https://javascript.plainenglish.io/6-caching-strategies-to-remember-while-designing-cache-system-da058a3757cf 캐시 시스템 관련 용어, 읽기 vs 쓰기 중심의 애플리케이션에서 캐싱 전략, 캐시를 무효화하는 방법 등에 대해서 알려드리겠습니다. 개요 캐싱의 목표는 원본 소스에서 데이터를 가져오는 횟수를 줄여 처리 속도를 높이고 대기 시간을 감소시키는 것입니다. 캐싱은 인메모리 캐싱, 디스크 캐싱, 데이터베이스 캐싱, CDN 캐싱과 같은 다양한 수준의 아키텍처에서 구현될 수 있습니다. 데이터는 각각 고유한 이점이 있는 다양한 기술을 사용하여 캐싱할 수 있습니다. 인메모리 캐싱은 컴퓨터의 주 메모리에 데이터를 저장하여 디스크 저장소 보다 빠른 액세스를 제공합니다. 반면 디스크 캐싱은 하드 디스크에 데이터를 저장하므로 주 메모리보다는 느리지만 원격 소스에서 데이터를 가져오는 것보다 빠릅니다. 데이터…","fields":{"slug":"/cs/6-caching-strategies/"},"frontmatter":{"categories":"translate cs","title":"(번역) 캐시 시스템 설계할 때 기억해야 할 6가지 캐싱 전략","date":"June 13, 2023","draft":false}},"next":{"fields":{"slug":"/javascript/metaprogramming/"}},"previous":{"fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"}}}]}}, + "result": {"pageContext":{"currentCategory":"cs","categories":["All","framework","react","next","translate","typescript","browser","infra","a11y","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"44fd3704-304a-5b2a-8702-af196e247d28","excerpt":"원글: https://github.com/kamranahmedse/design-patterns-for-humans/blob/master/readme.md 역자주: 이 글은 Kamran Ahmed의 글이며, PHP-7 기준으로 작성된 예제 코드를 타입스크립트로 변경하였습니다. logo 🎉 디자인 패턴에 대한 초간단 설명입니다! 🎉 누구든지 혼란스럽게 만들 수 있는 주제입니다. 이 글에서는 최대한 간단한 방식으로 설명함으로써 당신의 기억 속 (그리고 아마도 제 기억)에 각인시키기 위해 노력할 것입니다. 저의 다른 프로젝트도 확인하고 트위터에서 “안녕”이라고 인사해 보세요. 생성 패턴 구조 패턴 행동 패턴 심플 팩토리(Simple Factory) 어댑터(Adapter) 책임 연쇄(Chain of Responsibility) 팩토리 메서드(Factory Method) 브릿지(Bridge) 명령(Command) 추상 팩토리(Abstract Factory) 컴포지트(Composite) 반복자…","fields":{"slug":"/cs/design-patterns-for-humans/"},"frontmatter":{"categories":"translate cs","title":"(번역) 우리들을 위한 디자인 패턴","date":"August 27, 2023","draft":false}},"next":{"fields":{"slug":"/javascript/garbage-collection/"}},"previous":{"fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"}}},{"node":{"id":"872218ff-8507-5910-8001-bc52d72fca27","excerpt":"원글: https://javascript.plainenglish.io/6-caching-strategies-to-remember-while-designing-cache-system-da058a3757cf 캐시 시스템 관련 용어, 읽기 vs 쓰기 중심의 애플리케이션에서 캐싱 전략, 캐시를 무효화하는 방법 등에 대해서 알려드리겠습니다. 개요 캐싱의 목표는 원본 소스에서 데이터를 가져오는 횟수를 줄여 처리 속도를 높이고 대기 시간을 감소시키는 것입니다. 캐싱은 인메모리 캐싱, 디스크 캐싱, 데이터베이스 캐싱, CDN 캐싱과 같은 다양한 수준의 아키텍처에서 구현될 수 있습니다. 데이터는 각각 고유한 이점이 있는 다양한 기술을 사용하여 캐싱할 수 있습니다. 인메모리 캐싱은 컴퓨터의 주 메모리에 데이터를 저장하여 디스크 저장소 보다 빠른 액세스를 제공합니다. 반면 디스크 캐싱은 하드 디스크에 데이터를 저장하므로 주 메모리보다는 느리지만 원격 소스에서 데이터를 가져오는 것보다 빠릅니다. 데이터…","fields":{"slug":"/cs/6-caching-strategies/"},"frontmatter":{"categories":"translate cs","title":"(번역) 캐시 시스템 설계할 때 기억해야 할 6가지 캐싱 전략","date":"June 13, 2023","draft":false}},"next":{"fields":{"slug":"/javascript/metaprogramming/"}},"previous":{"fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"}}}]}}, "staticQueryHashes": ["1073350324","1956554647","2938748437"]} \ No newline at end of file diff --git a/page-data/posts/feature/page-data.json b/page-data/posts/feature/page-data.json index 18fb22e3..cb6f0aee 100644 --- a/page-data/posts/feature/page-data.json +++ b/page-data/posts/feature/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-category-template-js", "path": "/posts/feature", - "result": {"pageContext":{"currentCategory":"feature","categories":["All","framework","react","next","translate","typescript","browser","a11y","infra","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"7dd77d91-4c83-5f76-92b0-9e9b60089ea7","excerpt":"Date와 인연이 깊은지 날짜 선택 UI를 custom 해서 개발해야 하는 경험을 두 번째 하게 되었다. mui datepicker를 붙이기엔 너무 과해서 react-calendar 라이브러리를 사용하려고 했는데, 문제는 input은 함께 딸려오지 않기 때문에 input 관련 interaction은 직접 구현해야 했다. 인 하우스용 서비스라 여러 라이브러리 참고해서 직접 인터렉션을 정의하고 구현해 보았는데 기록 차 경험을 남겨본다! TL;DR; 전체 구현 결과물은 code sandbox에서 확인할 수 있다. DatePicker 컴포넌트를 개발해 보자! (calendar는 라이브러리를 사용) 딱히 input에 대한 큰 요구사항이 없이 기능 개발을 해야 하는 경우 그냥 react-date-picker를 써라 DatePicker 컴포넌트 개발 dependency react-calendar dayjs props 설계 minDate, maxDate: date range를 지원해야하므로 …","fields":{"slug":"/feature/input-date/"},"frontmatter":{"categories":"feature","title":"날짜 입력 input 인터렉션 개발기","date":"April 23, 2023","draft":false}},"next":{"fields":{"slug":"/react/recoil-introduction/"}},"previous":{"fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"}}},{"node":{"id":"47a3c1ed-8f39-56be-81c1-c0cd470b4c41","excerpt":"문제상황 dropdown 컴포넌트를 만들었는데, focus가 다른곳으로 이동하면 펼쳐져있던 dropdown이 접혀져야 해서 blur 이벤트 발생시 접히도록 했다. 그랬더니 펼쳐져 있는 항목 중 하나를 click 했을때, 위에서 설정한 blur 이벤트가 먼저 발생하면서 click 이벤트가 호출되지 않는 이슈가 문제였다. dropdown_비정상동작.gif 문제의 코드 TL;DR Touch, mouse 이벤트 순서 touchstart touchmove touchend mouseover mousemove mousedown mouseup click 해결방법 click시 처리해줘야 하는 이벤트 핸들러를 click 이 아닌 mousedown 에서 처리해준다. dropdown_정상동작.gif Dropdown CodeSandbox Touch and mouse 이벤트 그러면 지금부터는 web.dev 문서를 살펴보면서 알게된 touch 이벤트의 도입 배경과 touch, mouse 이벤트를 함께 다루기…","fields":{"slug":"/browser/touch-mouse-event/"},"frontmatter":{"categories":"feature browser","title":"Touch, Mouse 이벤트 함께 다루기 (feat. dropdown)","date":"September 18, 2022","draft":false}},"next":{"fields":{"slug":"/infra/jenkins-bitbucket/"}},"previous":{"fields":{"slug":"/react/error-handle/"}}},{"node":{"id":"2b300262-3404-5526-8a92-b3c887f4e178","excerpt":"보통 어플리케이션에서 공통된 디자인의 confirm 창을 사용한다. 이때, window.confirm과 같이 고객의 OK/CANCEL 클릭 여부에 따라서 다음 동작을 이어나가게 하고싶다면 어떻게 구현해야할까? 준비물 Modal Component (global 하게 등록) Global State Modal Hook 보통 웹 어플리케이션에서는 공통된 디자인의 confirm을 사용하므로 화면에 보여지는 역할을 하는 Modal Component가 필요하다. 이 컴포넌트는 어플리케이션 전반에 걸쳐서 자주 사용하는 라이브러리이므로 App.js에서 global 하게 로드한다. global 하게 등록된 Modal 컴포넌트를 제어하기 위해서는 어플리케이션 어느 곳에서든 제어 가능하게 하기 위해서 global state가 필요하다. 그래서 modal 관련 state가 변경되었을때, Modal 컴포넌트가 render 되게 한다. 이제 window.confirm 처럼 사용하기 위해 confirm 함수를…","fields":{"slug":"/feature/confirm/"},"frontmatter":{"categories":"feature","title":"Promise를 사용하여 window.confirm 구현하기","date":"August 19, 2022","draft":false}},"next":{"fields":{"slug":"/test/test-introduction/"}},"previous":{"fields":{"slug":"/infra/jenkins-bitbucket/"}}}]}}, + "result": {"pageContext":{"currentCategory":"feature","categories":["All","framework","react","next","translate","typescript","browser","infra","a11y","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"7dd77d91-4c83-5f76-92b0-9e9b60089ea7","excerpt":"Date와 인연이 깊은지 날짜 선택 UI를 custom 해서 개발해야 하는 경험을 두 번째 하게 되었다. mui datepicker를 붙이기엔 너무 과해서 react-calendar 라이브러리를 사용하려고 했는데, 문제는 input은 함께 딸려오지 않기 때문에 input 관련 interaction은 직접 구현해야 했다. 인 하우스용 서비스라 여러 라이브러리 참고해서 직접 인터렉션을 정의하고 구현해 보았는데 기록 차 경험을 남겨본다! TL;DR; 전체 구현 결과물은 code sandbox에서 확인할 수 있다. DatePicker 컴포넌트를 개발해 보자! (calendar는 라이브러리를 사용) 딱히 input에 대한 큰 요구사항이 없이 기능 개발을 해야 하는 경우 그냥 react-date-picker를 써라 DatePicker 컴포넌트 개발 dependency react-calendar dayjs props 설계 minDate, maxDate: date range를 지원해야하므로 …","fields":{"slug":"/feature/input-date/"},"frontmatter":{"categories":"feature","title":"날짜 입력 input 인터렉션 개발기","date":"April 23, 2023","draft":false}},"next":{"fields":{"slug":"/react/recoil-introduction/"}},"previous":{"fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"}}},{"node":{"id":"47a3c1ed-8f39-56be-81c1-c0cd470b4c41","excerpt":"문제상황 dropdown 컴포넌트를 만들었는데, focus가 다른곳으로 이동하면 펼쳐져있던 dropdown이 접혀져야 해서 blur 이벤트 발생시 접히도록 했다. 그랬더니 펼쳐져 있는 항목 중 하나를 click 했을때, 위에서 설정한 blur 이벤트가 먼저 발생하면서 click 이벤트가 호출되지 않는 이슈가 문제였다. dropdown_비정상동작.gif 문제의 코드 TL;DR Touch, mouse 이벤트 순서 touchstart touchmove touchend mouseover mousemove mousedown mouseup click 해결방법 click시 처리해줘야 하는 이벤트 핸들러를 click 이 아닌 mousedown 에서 처리해준다. dropdown_정상동작.gif Dropdown CodeSandbox Touch and mouse 이벤트 그러면 지금부터는 web.dev 문서를 살펴보면서 알게된 touch 이벤트의 도입 배경과 touch, mouse 이벤트를 함께 다루기…","fields":{"slug":"/browser/touch-mouse-event/"},"frontmatter":{"categories":"feature browser","title":"Touch, Mouse 이벤트 함께 다루기 (feat. dropdown)","date":"September 18, 2022","draft":false}},"next":{"fields":{"slug":"/infra/jenkins-bitbucket/"}},"previous":{"fields":{"slug":"/react/error-handle/"}}},{"node":{"id":"2b300262-3404-5526-8a92-b3c887f4e178","excerpt":"보통 어플리케이션에서 공통된 디자인의 confirm 창을 사용한다. 이때, window.confirm과 같이 고객의 OK/CANCEL 클릭 여부에 따라서 다음 동작을 이어나가게 하고싶다면 어떻게 구현해야할까? 준비물 Modal Component (global 하게 등록) Global State Modal Hook 보통 웹 어플리케이션에서는 공통된 디자인의 confirm을 사용하므로 화면에 보여지는 역할을 하는 Modal Component가 필요하다. 이 컴포넌트는 어플리케이션 전반에 걸쳐서 자주 사용하는 라이브러리이므로 App.js에서 global 하게 로드한다. global 하게 등록된 Modal 컴포넌트를 제어하기 위해서는 어플리케이션 어느 곳에서든 제어 가능하게 하기 위해서 global state가 필요하다. 그래서 modal 관련 state가 변경되었을때, Modal 컴포넌트가 render 되게 한다. 이제 window.confirm 처럼 사용하기 위해 confirm 함수를…","fields":{"slug":"/feature/confirm/"},"frontmatter":{"categories":"feature","title":"Promise를 사용하여 window.confirm 구현하기","date":"August 19, 2022","draft":false}},"next":{"fields":{"slug":"/test/test-introduction/"}},"previous":{"fields":{"slug":"/infra/jenkins-bitbucket/"}}}]}}, "staticQueryHashes": ["1073350324","1956554647","2938748437"]} \ No newline at end of file diff --git a/page-data/posts/framework/page-data.json b/page-data/posts/framework/page-data.json index 983bebc8..8dc51e56 100644 --- a/page-data/posts/framework/page-data.json +++ b/page-data/posts/framework/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-category-template-js", "path": "/posts/framework", - "result": {"pageContext":{"currentCategory":"framework","categories":["All","framework","react","next","translate","typescript","browser","a11y","infra","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"ee4fa349-0d3c-533a-9bcf-38d5b6c48497","excerpt":"TL;DR; TypeScript와 ESLint는 “의도적으로 무시”하는 코드에 대해 다른 접근 방식을 가집니다. TypeScript는 언더스코어(_) 접두사로 시작하는 변수를 자동으로 무시하지만, ESLint의 no-unused-vars 규칙은 기본적으로 더 엄격하므로, 언더스코어로 시작하는 변수도 사용되지 않은 것으로 간주하여 오류를 발생시킵니다. 개발자는 ESLint 설정을 통해 “의도적으로 무시”하는 코드에 대해 TypeScript와 유사한 동작을 구현하거나 더 세밀한 제어를 할 수 있습니다. “의도적으로 무시”하는 코드 아래 예시와 같은 코드를 작성해본 경험이 있으신가요? 우리는 가끔 “의도적으로 무시” 하는 것으로 간주하는 코드를 작성해야 할 때가 있습니다. 루프 인덱스 무시: _index는 사용되지 않지만, entries() 메서드의 반환 값을 구조 분해하기 위해 필요 콜백 함수에서 매개변수 무시: _item은 사용되지 않지만, index에 접근하기 위해 선언 구조 분…","fields":{"slug":"/typescript-eslint-handling-intentionally-ignored-variables/"},"frontmatter":{"categories":"framework","title":"‘의도적으로 무시’하는 코드에 대한 TypeScript와 ESLint의 다른 처리 방식","date":"October 26, 2024","draft":false}},"next":{"fields":{"slug":"/astro-hydration/"}},"previous":null},{"node":{"id":"87e0c0f2-6c03-513a-99a6-ef53d6389653","excerpt":"Astro는 애플리케이션을 구축이 아닌 콘텐츠에 중점을 두고 설계된 정적 사이트 빌더입니다. 따라서 블로그, 포트폴리오, 이벤트용 마케팅 성격의 사이트 개발에 매우 적합한 프레임워크 입니다. Astro 컴포넌트에 대해 먼저 간략히 알아보고, 어떻게 여러 프레임워크(React, Preact,Svelte, Vue, SolidJS)를 하나의 프로젝트에서 혼합해서 사용할 수 있는지 알아보도록 하겠습니다. Astro 컴포넌트 Astro 컴포넌트는 모든 Astro 프로젝트의 기본 구성 요소입니다. 이는 클라이언트 측 런타임이 없는 HTML 전용 템플릿 컴포넌트로 .astro 파일 확장자 사용합니다. Astro 컴포넌트에 대해 알아야 할 가장 중요한 점은 클라이언트에서 렌더링되지 않는다는 것입니다. HTML로 렌더링되며, 컴포넌트 스크립트는 브라우저로 전송되는 최종 페이지에서 모두 제거됩니다. 구성(스크립트 + 템플릿) .astro 파일은 스크립트와 템플릿으로 구성됩니다. src/compone…","fields":{"slug":"/astro-hydration/"},"frontmatter":{"categories":"framework","title":"Astro로 알아보는 Selective & Progressive Hydration","date":"October 13, 2024","draft":false}},"next":{"fields":{"slug":"/react/process-env-destructuring-error.md/"}},"previous":{"fields":{"slug":"/typescript-eslint-handling-intentionally-ignored-variables/"}}}]}}, + "result": {"pageContext":{"currentCategory":"framework","categories":["All","framework","react","next","translate","typescript","browser","infra","a11y","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"ee4fa349-0d3c-533a-9bcf-38d5b6c48497","excerpt":"TL;DR; TypeScript와 ESLint는 “의도적으로 무시”하는 코드에 대해 다른 접근 방식을 가집니다. TypeScript는 언더스코어(_) 접두사로 시작하는 변수를 자동으로 무시하지만, ESLint의 no-unused-vars 규칙은 기본적으로 더 엄격하므로, 언더스코어로 시작하는 변수도 사용되지 않은 것으로 간주하여 오류를 발생시킵니다. 개발자는 ESLint 설정을 통해 “의도적으로 무시”하는 코드에 대해 TypeScript와 유사한 동작을 구현하거나 더 세밀한 제어를 할 수 있습니다. “의도적으로 무시”하는 코드 아래 예시와 같은 코드를 작성해본 경험이 있으신가요? 우리는 가끔 “의도적으로 무시” 하는 것으로 간주하는 코드를 작성해야 할 때가 있습니다. 루프 인덱스 무시: _index는 사용되지 않지만, entries() 메서드의 반환 값을 구조 분해하기 위해 필요 콜백 함수에서 매개변수 무시: _item은 사용되지 않지만, index에 접근하기 위해 선언 구조 분…","fields":{"slug":"/typescript-eslint-handling-intentionally-ignored-variables/"},"frontmatter":{"categories":"framework","title":"‘의도적으로 무시’하는 코드에 대한 TypeScript와 ESLint의 다른 처리 방식","date":"October 26, 2024","draft":false}},"next":{"fields":{"slug":"/astro-hydration/"}},"previous":null},{"node":{"id":"87e0c0f2-6c03-513a-99a6-ef53d6389653","excerpt":"Astro는 애플리케이션을 구축이 아닌 콘텐츠에 중점을 두고 설계된 정적 사이트 빌더입니다. 따라서 블로그, 포트폴리오, 이벤트용 마케팅 성격의 사이트 개발에 매우 적합한 프레임워크 입니다. Astro 컴포넌트에 대해 먼저 간략히 알아보고, 어떻게 여러 프레임워크(React, Preact,Svelte, Vue, SolidJS)를 하나의 프로젝트에서 혼합해서 사용할 수 있는지 알아보도록 하겠습니다. Astro 컴포넌트 Astro 컴포넌트는 모든 Astro 프로젝트의 기본 구성 요소입니다. 이는 클라이언트 측 런타임이 없는 HTML 전용 템플릿 컴포넌트로 .astro 파일 확장자 사용합니다. Astro 컴포넌트에 대해 알아야 할 가장 중요한 점은 클라이언트에서 렌더링되지 않는다는 것입니다. HTML로 렌더링되며, 컴포넌트 스크립트는 브라우저로 전송되는 최종 페이지에서 모두 제거됩니다. 구성(스크립트 + 템플릿) .astro 파일은 스크립트와 템플릿으로 구성됩니다. src/compone…","fields":{"slug":"/astro-hydration/"},"frontmatter":{"categories":"framework","title":"Astro로 알아보는 Selective & Progressive Hydration","date":"October 13, 2024","draft":false}},"next":{"fields":{"slug":"/react/process-env-destructuring-error.md/"}},"previous":{"fields":{"slug":"/typescript-eslint-handling-intentionally-ignored-variables/"}}}]}}, "staticQueryHashes": ["1073350324","1956554647","2938748437"]} \ No newline at end of file diff --git a/page-data/posts/infra/page-data.json b/page-data/posts/infra/page-data.json index 5d010c2e..e8eda0bd 100644 --- a/page-data/posts/infra/page-data.json +++ b/page-data/posts/infra/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-category-template-js", "path": "/posts/infra", - "result": {"pageContext":{"currentCategory":"infra","categories":["All","framework","react","next","translate","typescript","browser","a11y","infra","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"4cbf47cf-4a24-5ffd-88c3-bdede11e8a72","excerpt":"Docker 엔진 시작하기/종료하기 도커 엔진: 자동 실행 설정 활성화시, 컴퓨터를 켰을 때 함께 자동으로 실행 Docker Desktop은 도커 엔진 자동으로 실행하도록 설정 되어있음. 리눅스 명령어로 엔진 시작, 종료, 자동 실행 설정 방법 systemctrl: 서비스 시작 및 중지 관련 리눅스 명령어 (system control) 컨테이너: 실행시킬 스크립트가 따로 필요. 컴퓨터를 켰을 때 함께 자동으로 실행 되지 않음. 컨테이너의 기본적인 사용 방법 컨테이너를 다루는 모든 명령어는 docker 명령어로 시작 도커 명령어 구성: docker 커맨드(무엇을 어떻게) 대상 + 옵션, 인자: docker 커맨드(무엇을 어떻게) 옵션 대상 인자 상위 커맨드는 생략 가능하다?! ex) docker run, docker start 커맨드(무엇을 어떻게)는 상위커맨드와 하위커맨드로 나뉘어짐. container 상위 커맨들를 생략하는 경우가 있음. 도커 1.13부터 커맨드가 재편되면서 …","fields":{"slug":"/infra/docker-command/"},"frontmatter":{"categories":"infra","title":"docker에서 자주쓰는 명령어","date":"February 18, 2024","draft":false}},"next":{"fields":{"slug":"/react/storybook-react-server-components/"}},"previous":{"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}}},{"node":{"id":"bacf9614-be80-5449-8b45-fa90310ef8dd","excerpt":"목표 Jenkins pipeline을 이용하여, bitbucket에서 PR 생성시 Jenkins job을 실행시키고 그 결과를 slack으로 전송한다. (Jenkins 내의 credential 설정이나 자세한 Jenkins 플러그인 설정 사항은 해당 글에서 포함하지 않음.) Webhook 웹훅이란 뭘까? 위의 목표를 이루려면 우리는 웹훅이 필요하다. 어플리케이션에 한정하여 특정 이벤트가 발생할 때 지정된 URL로 push 알림을 보내는 것이 webhook이다. callback 이랑 비슷한데, 조금 더 작은 범위라고 생각하면 된다. 이 방법은 주기적으로 검사하여 변경 사항이 있는지 확인하는 것보다 훨씬 효율적이다. 구현 방법 우리는 bitbucket에서 PR이 생성되었을때 Jenkins 플러그인인 Generic Webhook Trigger를 호출 하고, Jenkins Job 실행이 끝났을 때 slack Webhook을 호출 할 것이다. 1. Jekins에서 Generic Webho…","fields":{"slug":"/infra/jenkins-bitbucket/"},"frontmatter":{"categories":"infra","title":"Bitbucket과 Jenkins를 연동하여 테스트 자동화 (feat. Generic Webhook Trigger)","date":"September 04, 2022","draft":false}},"next":{"fields":{"slug":"/feature/confirm/"}},"previous":{"fields":{"slug":"/browser/touch-mouse-event/"}}}]}}, + "result": {"pageContext":{"currentCategory":"infra","categories":["All","framework","react","next","translate","typescript","browser","infra","a11y","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"4cbf47cf-4a24-5ffd-88c3-bdede11e8a72","excerpt":"Docker 엔진 시작하기/종료하기 도커 엔진: 자동 실행 설정 활성화시, 컴퓨터를 켰을 때 함께 자동으로 실행 Docker Desktop은 도커 엔진 자동으로 실행하도록 설정 되어있음. 리눅스 명령어로 엔진 시작, 종료, 자동 실행 설정 방법 systemctrl: 서비스 시작 및 중지 관련 리눅스 명령어 (system control) 컨테이너: 실행시킬 스크립트가 따로 필요. 컴퓨터를 켰을 때 함께 자동으로 실행 되지 않음. 컨테이너의 기본적인 사용 방법 컨테이너를 다루는 모든 명령어는 docker 명령어로 시작 도커 명령어 구성: docker 커맨드(무엇을 어떻게) 대상 + 옵션, 인자: docker 커맨드(무엇을 어떻게) 옵션 대상 인자 상위 커맨드는 생략 가능하다?! ex) docker run, docker start 커맨드(무엇을 어떻게)는 상위커맨드와 하위커맨드로 나뉘어짐. container 상위 커맨들를 생략하는 경우가 있음. 도커 1.13부터 커맨드가 재편되면서 …","fields":{"slug":"/infra/docker-command/"},"frontmatter":{"categories":"infra","title":"docker에서 자주쓰는 명령어","date":"February 18, 2024","draft":false}},"next":{"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}},"previous":{"fields":{"slug":"/browser/browser-coordinate-size-api/"}}},{"node":{"id":"bacf9614-be80-5449-8b45-fa90310ef8dd","excerpt":"목표 Jenkins pipeline을 이용하여, bitbucket에서 PR 생성시 Jenkins job을 실행시키고 그 결과를 slack으로 전송한다. (Jenkins 내의 credential 설정이나 자세한 Jenkins 플러그인 설정 사항은 해당 글에서 포함하지 않음.) Webhook 웹훅이란 뭘까? 위의 목표를 이루려면 우리는 웹훅이 필요하다. 어플리케이션에 한정하여 특정 이벤트가 발생할 때 지정된 URL로 push 알림을 보내는 것이 webhook이다. callback 이랑 비슷한데, 조금 더 작은 범위라고 생각하면 된다. 이 방법은 주기적으로 검사하여 변경 사항이 있는지 확인하는 것보다 훨씬 효율적이다. 구현 방법 우리는 bitbucket에서 PR이 생성되었을때 Jenkins 플러그인인 Generic Webhook Trigger를 호출 하고, Jenkins Job 실행이 끝났을 때 slack Webhook을 호출 할 것이다. 1. Jekins에서 Generic Webho…","fields":{"slug":"/infra/jenkins-bitbucket/"},"frontmatter":{"categories":"infra","title":"Bitbucket과 Jenkins를 연동하여 테스트 자동화 (feat. Generic Webhook Trigger)","date":"September 04, 2022","draft":false}},"next":{"fields":{"slug":"/feature/confirm/"}},"previous":{"fields":{"slug":"/browser/touch-mouse-event/"}}}]}}, "staticQueryHashes": ["1073350324","1956554647","2938748437"]} \ No newline at end of file diff --git a/page-data/posts/javascript/page-data.json b/page-data/posts/javascript/page-data.json index 157c082b..867635c3 100644 --- a/page-data/posts/javascript/page-data.json +++ b/page-data/posts/javascript/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-category-template-js", "path": "/posts/javascript", - "result": {"pageContext":{"currentCategory":"javascript","categories":["All","framework","react","next","translate","typescript","browser","a11y","infra","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"55331269-8b15-516d-aa6d-7eb90d2fedee","excerpt":"원문: https://javascript.plainenglish.io/the-5-most-innovative-proposals-for-javascript-planned-for-2024-22139dd2f546 JavaScript Logo 2024 자바스크립트가 빠른 속도로 발전함에 따라 2024년은 이 프로그래밍 언어에 있어 중요한 해가 될 것입니다. 이 글에서는 개발자들이 자바스크립트 코딩에 접근하는 방식을 재정의하기 위한 최신 제안을 자세히 살펴보겠습니다. 이 글이 유용하길 바랍니다! 시작해 봅시다! 목차 데코레이터 Temporal API 파이프라인 연산자 Error cause 레코드와 튜플 1. 데코레이터 자바스크립트에 데코레이터가 도입된 것은 개발자가 클래스, 메서드, 프로퍼티 및 매개변수의 동작을 조작하고 개선할 수 있는 방법이 크게 발전했음을 의미합니다. 파이썬과 타입스크립트와 같은 다른 프로그래밍 언어에서 유래한 데코레이터는 기능을 추가하거나 수정하는 간결하고 선언적인…","fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"},"frontmatter":{"categories":"translate javascript","title":"(번역) 2024년 자바스크립트의 5가지 가장 혁신적인 제안","date":"December 20, 2023","draft":false}},"next":{"fields":{"slug":"/react/server-rendering-and-react-query/"}},"previous":{"fields":{"slug":"/react/next-app-router-react-query/"}}},{"node":{"id":"7e56bfba-a382-5266-96c6-f581980fe75d","excerpt":"힙과 스택 메모리의 차이를 살펴보고, 자바스크립트 대표 엔진인 V8의 Resident set 구조와 Minor GC, Major GC 의 동작 알고리즘에 대해서 자세하게 살펴 보도록 하겠습니다. 이 글이 가비지 컬렉터가 어떻게 동작하는지 이해하는데 도움이 되길 바랍니다. V8 메모리 구조 (feat. Resident set) Resident Set은 실행 중인 프로그램의 메모리 관리를 위해 사용되는 용어로, 현재 V8 프로세스가 사용하는 모든 메모리를 나타냅니다. 이것은 JavaScript 코드, 객체, 함수, 변수 등을 저장하는 데 필요한 모든 메모리를 포함합니다. Resident Set은 다음과 같이 여러 세그먼트로 구성됩니다. \n출처: memory management in V8 stack vs heap 메모리 공간은 크게 스택 메모리(Stack memory)와 힙 메모리(Heap memory)로 구분되며, 힙 메모리는 다양한 목적으로 서비스하는 여러 공간으로 더 나누어집니다.…","fields":{"slug":"/javascript/garbage-collection/"},"frontmatter":{"categories":"javascript","title":"자바스크립트 가비지 컬렉션 알고리즘","date":"July 14, 2023","draft":false}},"next":{"fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"}},"previous":{"fields":{"slug":"/cs/design-patterns-for-humans/"}}},{"node":{"id":"7268c9ab-b8e9-5638-aa7a-c6c2e2d4f1a8","excerpt":"ES6에서는 Proxy, Reflect 객체를 통해 메타프로그래밍을 할 수 있습니다. 메타프로그래밍이 무엇인지? 그리고 Proxy와 Reflect에 대해서 알아보도록 하겠습니다. Metaprogramming 메타프로그래밍은 프로그램이 자기 자신을 조작할 수 있는 능력을 말합니다. 이를 통해 프로그램은 동적으로 자기 자신을 변화시키거나, 다른 프로그램을 분석하거나, 새로운 코드를 생성할 수 있습니다. 메타프로그래밍을 통해 코드의 유연성과 재사용성을 높일 수 있으며, 런타임에서 객체의 동작을 수정할 수 있어서 더욱 강력한 코드를 작성할 수 있습니다. 메타프로그래밍에서 가장 중요한 세 가지 개념 Introspection Introspection은 코드가 자기 자신을 검사할 수 있는 능력을 의미합니다. 이를 통해 코드의 내부 구조와 데이터를 검사하거나, 코드의 실행 시점에서 객체의 메소드나 프로퍼티에 접근할 수 있습니다. 자바스크립트에서는 ES6부터 Reflect 객체를 제공하여 Intr…","fields":{"slug":"/javascript/metaprogramming/"},"frontmatter":{"categories":"javascript","title":"Proxy, Reflect와 메타프로그래밍","date":"May 07, 2023","draft":false}},"next":{"fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"}},"previous":{"fields":{"slug":"/cs/6-caching-strategies/"}}},{"node":{"id":"09bde05f-002e-5468-8934-6141fab54678","excerpt":"원문: https://blog.openreplay.com/deep-copying-objects-with-the-structuredclone-api/ Deep Copying Objects with the StructuredClone API 개요: 자바스크립트에서 객체를 복사하는 것은 간단하지 않으며, 이는 잘 알려진 문제입니다. 그러나 이 글에서는 해결책을 제공합니다. StructuredClone API를 사용하면 모든 객체를 간단하고 빠르게 복사할 수 있습니다. 자바스크립트에서 객체가 변수에 저장될 때, 해당 변수는 객체의 참조값을 갖습니다. 이는 변수 자체에 객체를 저장하는 것이 아니라, 객체의 메모리 위치를 나타내는 식별자를 저장한다는 것을 의미합니다. 객체의 복사는 원시 타입과는 다른 방식으로 동작합니다. 얕은 복사(Shallow Copy) vs. 깊은 복사(Deep Copy) 자바스크립트에서 값은 두 가지 방법으로 복사할 수 있습니다. 얕은 복사와 깊은 복사입니다. 얕은 복…","fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"},"frontmatter":{"categories":"translate javascript","title":"(번역) StructuredClone API를 사용하여 객체를 깊은 복사하는 법","date":"May 07, 2023","draft":false}},"next":{"fields":{"slug":"/feature/input-date/"}},"previous":{"fields":{"slug":"/javascript/metaprogramming/"}}}]}}, + "result": {"pageContext":{"currentCategory":"javascript","categories":["All","framework","react","next","translate","typescript","browser","infra","a11y","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"55331269-8b15-516d-aa6d-7eb90d2fedee","excerpt":"원문: https://javascript.plainenglish.io/the-5-most-innovative-proposals-for-javascript-planned-for-2024-22139dd2f546 JavaScript Logo 2024 자바스크립트가 빠른 속도로 발전함에 따라 2024년은 이 프로그래밍 언어에 있어 중요한 해가 될 것입니다. 이 글에서는 개발자들이 자바스크립트 코딩에 접근하는 방식을 재정의하기 위한 최신 제안을 자세히 살펴보겠습니다. 이 글이 유용하길 바랍니다! 시작해 봅시다! 목차 데코레이터 Temporal API 파이프라인 연산자 Error cause 레코드와 튜플 1. 데코레이터 자바스크립트에 데코레이터가 도입된 것은 개발자가 클래스, 메서드, 프로퍼티 및 매개변수의 동작을 조작하고 개선할 수 있는 방법이 크게 발전했음을 의미합니다. 파이썬과 타입스크립트와 같은 다른 프로그래밍 언어에서 유래한 데코레이터는 기능을 추가하거나 수정하는 간결하고 선언적인…","fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"},"frontmatter":{"categories":"translate javascript","title":"(번역) 2024년 자바스크립트의 5가지 가장 혁신적인 제안","date":"December 20, 2023","draft":false}},"next":{"fields":{"slug":"/react/server-rendering-and-react-query/"}},"previous":{"fields":{"slug":"/react/next-app-router-react-query/"}}},{"node":{"id":"7e56bfba-a382-5266-96c6-f581980fe75d","excerpt":"힙과 스택 메모리의 차이를 살펴보고, 자바스크립트 대표 엔진인 V8의 Resident set 구조와 Minor GC, Major GC 의 동작 알고리즘에 대해서 자세하게 살펴 보도록 하겠습니다. 이 글이 가비지 컬렉터가 어떻게 동작하는지 이해하는데 도움이 되길 바랍니다. V8 메모리 구조 (feat. Resident set) Resident Set은 실행 중인 프로그램의 메모리 관리를 위해 사용되는 용어로, 현재 V8 프로세스가 사용하는 모든 메모리를 나타냅니다. 이것은 JavaScript 코드, 객체, 함수, 변수 등을 저장하는 데 필요한 모든 메모리를 포함합니다. Resident Set은 다음과 같이 여러 세그먼트로 구성됩니다. \n출처: memory management in V8 stack vs heap 메모리 공간은 크게 스택 메모리(Stack memory)와 힙 메모리(Heap memory)로 구분되며, 힙 메모리는 다양한 목적으로 서비스하는 여러 공간으로 더 나누어집니다.…","fields":{"slug":"/javascript/garbage-collection/"},"frontmatter":{"categories":"javascript","title":"자바스크립트 가비지 컬렉션 알고리즘","date":"July 14, 2023","draft":false}},"next":{"fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"}},"previous":{"fields":{"slug":"/cs/design-patterns-for-humans/"}}},{"node":{"id":"7268c9ab-b8e9-5638-aa7a-c6c2e2d4f1a8","excerpt":"ES6에서는 Proxy, Reflect 객체를 통해 메타프로그래밍을 할 수 있습니다. 메타프로그래밍이 무엇인지? 그리고 Proxy와 Reflect에 대해서 알아보도록 하겠습니다. Metaprogramming 메타프로그래밍은 프로그램이 자기 자신을 조작할 수 있는 능력을 말합니다. 이를 통해 프로그램은 동적으로 자기 자신을 변화시키거나, 다른 프로그램을 분석하거나, 새로운 코드를 생성할 수 있습니다. 메타프로그래밍을 통해 코드의 유연성과 재사용성을 높일 수 있으며, 런타임에서 객체의 동작을 수정할 수 있어서 더욱 강력한 코드를 작성할 수 있습니다. 메타프로그래밍에서 가장 중요한 세 가지 개념 Introspection Introspection은 코드가 자기 자신을 검사할 수 있는 능력을 의미합니다. 이를 통해 코드의 내부 구조와 데이터를 검사하거나, 코드의 실행 시점에서 객체의 메소드나 프로퍼티에 접근할 수 있습니다. 자바스크립트에서는 ES6부터 Reflect 객체를 제공하여 Intr…","fields":{"slug":"/javascript/metaprogramming/"},"frontmatter":{"categories":"javascript","title":"Proxy, Reflect와 메타프로그래밍","date":"May 07, 2023","draft":false}},"next":{"fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"}},"previous":{"fields":{"slug":"/cs/6-caching-strategies/"}}},{"node":{"id":"09bde05f-002e-5468-8934-6141fab54678","excerpt":"원문: https://blog.openreplay.com/deep-copying-objects-with-the-structuredclone-api/ Deep Copying Objects with the StructuredClone API 개요: 자바스크립트에서 객체를 복사하는 것은 간단하지 않으며, 이는 잘 알려진 문제입니다. 그러나 이 글에서는 해결책을 제공합니다. StructuredClone API를 사용하면 모든 객체를 간단하고 빠르게 복사할 수 있습니다. 자바스크립트에서 객체가 변수에 저장될 때, 해당 변수는 객체의 참조값을 갖습니다. 이는 변수 자체에 객체를 저장하는 것이 아니라, 객체의 메모리 위치를 나타내는 식별자를 저장한다는 것을 의미합니다. 객체의 복사는 원시 타입과는 다른 방식으로 동작합니다. 얕은 복사(Shallow Copy) vs. 깊은 복사(Deep Copy) 자바스크립트에서 값은 두 가지 방법으로 복사할 수 있습니다. 얕은 복사와 깊은 복사입니다. 얕은 복…","fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"},"frontmatter":{"categories":"translate javascript","title":"(번역) StructuredClone API를 사용하여 객체를 깊은 복사하는 법","date":"May 07, 2023","draft":false}},"next":{"fields":{"slug":"/feature/input-date/"}},"previous":{"fields":{"slug":"/javascript/metaprogramming/"}}}]}}, "staticQueryHashes": ["1073350324","1956554647","2938748437"]} \ No newline at end of file diff --git a/page-data/posts/next/page-data.json b/page-data/posts/next/page-data.json index 54187c4e..7875140d 100644 --- a/page-data/posts/next/page-data.json +++ b/page-data/posts/next/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-category-template-js", "path": "/posts/next", - "result": {"pageContext":{"currentCategory":"next","categories":["All","framework","react","next","translate","typescript","browser","a11y","infra","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"909b8521-890b-5dc7-9c04-3289e2502910","excerpt":"process.env 값을 구조 분해 할당 하셨나요? 우리가 환경변수를 접근할 때 객체의 속성값에 접근할 때 사용하는 점 표기법을 사용하기 때문에 구조 분해 할당을 자연스럽게 사용해도 될 것 같은데요. 이런 경우, 왜 에러가 발생하는지 이번 글을 통해 알아보겠습니다. 문제 상황 다음과 같은 코드가 있다고 가정해 봅시다. 위 코드는 process.env 객체에서 API_KEY와 NEXT_PUBLIC_ANALYTICS_ID 변수를 구조 분해 할당하여 사용하려고 합니다. 그러나 Next.js에서는 이 코드가 제대로 작동하지 않을 수 있고, 오류가 발생할 수 있습니다. 왜 이런 문제가 발생할까요? process.env는 일반적인 자바스크립트 객체와는 다르기 때문입니다. 이는 Node.js 환경에서 환경 변수를 담고 있는 특수한 객체로, 모든 속성이 문자열 형태로 저장됩니다. Next.js는 빌드 타임과 런타임에서 환경 변수에 접근하는 방식이 다르므로, 이로 인해 구조 분해 할당을 사용할 …","fields":{"slug":"/react/process-env-destructuring-error.md/"},"frontmatter":{"categories":"react next","title":"환경 변수(process.env)를 구조 분해 할당하면 안되는 이유","date":"June 26, 2024","draft":false}},"next":{"fields":{"slug":"/typescript/typescript-as-const/"}},"previous":{"fields":{"slug":"/astro-hydration/"}}}]}}, + "result": {"pageContext":{"currentCategory":"next","categories":["All","framework","react","next","translate","typescript","browser","infra","a11y","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"909b8521-890b-5dc7-9c04-3289e2502910","excerpt":"process.env 값을 구조 분해 할당 하셨나요? 우리가 환경변수를 접근할 때 객체의 속성값에 접근할 때 사용하는 점 표기법을 사용하기 때문에 구조 분해 할당을 자연스럽게 사용해도 될 것 같은데요. 이런 경우, 왜 에러가 발생하는지 이번 글을 통해 알아보겠습니다. 문제 상황 다음과 같은 코드가 있다고 가정해 봅시다. 위 코드는 process.env 객체에서 API_KEY와 NEXT_PUBLIC_ANALYTICS_ID 변수를 구조 분해 할당하여 사용하려고 합니다. 그러나 Next.js에서는 이 코드가 제대로 작동하지 않을 수 있고, 오류가 발생할 수 있습니다. 왜 이런 문제가 발생할까요? process.env는 일반적인 자바스크립트 객체와는 다르기 때문입니다. 이는 Node.js 환경에서 환경 변수를 담고 있는 특수한 객체로, 모든 속성이 문자열 형태로 저장됩니다. Next.js는 빌드 타임과 런타임에서 환경 변수에 접근하는 방식이 다르므로, 이로 인해 구조 분해 할당을 사용할 …","fields":{"slug":"/react/process-env-destructuring-error.md/"},"frontmatter":{"categories":"react next","title":"환경 변수(process.env)를 구조 분해 할당하면 안되는 이유","date":"June 26, 2024","draft":false}},"next":{"fields":{"slug":"/typescript/typescript-as-const/"}},"previous":{"fields":{"slug":"/astro-hydration/"}}}]}}, "staticQueryHashes": ["1073350324","1956554647","2938748437"]} \ No newline at end of file diff --git a/page-data/posts/page-data.json b/page-data/posts/page-data.json index 78e4e5e7..ba93160b 100644 --- a/page-data/posts/page-data.json +++ b/page-data/posts/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-category-template-js", "path": "/posts", - "result": {"pageContext":{"currentCategory":"All","edges":[{"node":{"id":"ee4fa349-0d3c-533a-9bcf-38d5b6c48497","excerpt":"TL;DR; TypeScript와 ESLint는 “의도적으로 무시”하는 코드에 대해 다른 접근 방식을 가집니다. TypeScript는 언더스코어(_) 접두사로 시작하는 변수를 자동으로 무시하지만, ESLint의 no-unused-vars 규칙은 기본적으로 더 엄격하므로, 언더스코어로 시작하는 변수도 사용되지 않은 것으로 간주하여 오류를 발생시킵니다. 개발자는 ESLint 설정을 통해 “의도적으로 무시”하는 코드에 대해 TypeScript와 유사한 동작을 구현하거나 더 세밀한 제어를 할 수 있습니다. “의도적으로 무시”하는 코드 아래 예시와 같은 코드를 작성해본 경험이 있으신가요? 우리는 가끔 “의도적으로 무시” 하는 것으로 간주하는 코드를 작성해야 할 때가 있습니다. 루프 인덱스 무시: _index는 사용되지 않지만, entries() 메서드의 반환 값을 구조 분해하기 위해 필요 콜백 함수에서 매개변수 무시: _item은 사용되지 않지만, index에 접근하기 위해 선언 구조 분…","fields":{"slug":"/typescript-eslint-handling-intentionally-ignored-variables/"},"frontmatter":{"categories":"framework","title":"‘의도적으로 무시’하는 코드에 대한 TypeScript와 ESLint의 다른 처리 방식","date":"October 26, 2024","draft":false}},"next":{"fields":{"slug":"/astro-hydration/"}},"previous":null},{"node":{"id":"87e0c0f2-6c03-513a-99a6-ef53d6389653","excerpt":"Astro는 애플리케이션을 구축이 아닌 콘텐츠에 중점을 두고 설계된 정적 사이트 빌더입니다. 따라서 블로그, 포트폴리오, 이벤트용 마케팅 성격의 사이트 개발에 매우 적합한 프레임워크 입니다. Astro 컴포넌트에 대해 먼저 간략히 알아보고, 어떻게 여러 프레임워크(React, Preact,Svelte, Vue, SolidJS)를 하나의 프로젝트에서 혼합해서 사용할 수 있는지 알아보도록 하겠습니다. Astro 컴포넌트 Astro 컴포넌트는 모든 Astro 프로젝트의 기본 구성 요소입니다. 이는 클라이언트 측 런타임이 없는 HTML 전용 템플릿 컴포넌트로 .astro 파일 확장자 사용합니다. Astro 컴포넌트에 대해 알아야 할 가장 중요한 점은 클라이언트에서 렌더링되지 않는다는 것입니다. HTML로 렌더링되며, 컴포넌트 스크립트는 브라우저로 전송되는 최종 페이지에서 모두 제거됩니다. 구성(스크립트 + 템플릿) .astro 파일은 스크립트와 템플릿으로 구성됩니다. src/compone…","fields":{"slug":"/astro-hydration/"},"frontmatter":{"categories":"framework","title":"Astro로 알아보는 Selective & Progressive Hydration","date":"October 13, 2024","draft":false}},"next":{"fields":{"slug":"/react/process-env-destructuring-error.md/"}},"previous":{"fields":{"slug":"/typescript-eslint-handling-intentionally-ignored-variables/"}}},{"node":{"id":"909b8521-890b-5dc7-9c04-3289e2502910","excerpt":"process.env 값을 구조 분해 할당 하셨나요? 우리가 환경변수를 접근할 때 객체의 속성값에 접근할 때 사용하는 점 표기법을 사용하기 때문에 구조 분해 할당을 자연스럽게 사용해도 될 것 같은데요. 이런 경우, 왜 에러가 발생하는지 이번 글을 통해 알아보겠습니다. 문제 상황 다음과 같은 코드가 있다고 가정해 봅시다. 위 코드는 process.env 객체에서 API_KEY와 NEXT_PUBLIC_ANALYTICS_ID 변수를 구조 분해 할당하여 사용하려고 합니다. 그러나 Next.js에서는 이 코드가 제대로 작동하지 않을 수 있고, 오류가 발생할 수 있습니다. 왜 이런 문제가 발생할까요? process.env는 일반적인 자바스크립트 객체와는 다르기 때문입니다. 이는 Node.js 환경에서 환경 변수를 담고 있는 특수한 객체로, 모든 속성이 문자열 형태로 저장됩니다. Next.js는 빌드 타임과 런타임에서 환경 변수에 접근하는 방식이 다르므로, 이로 인해 구조 분해 할당을 사용할 …","fields":{"slug":"/react/process-env-destructuring-error.md/"},"frontmatter":{"categories":"react next","title":"환경 변수(process.env)를 구조 분해 할당하면 안되는 이유","date":"June 26, 2024","draft":false}},"next":{"fields":{"slug":"/typescript/typescript-as-const/"}},"previous":{"fields":{"slug":"/astro-hydration/"}}},{"node":{"id":"7af1f27b-4f32-56a8-8140-5191c8ba3150","excerpt":"원글: https://www.omarileon.me/blog/typescript-as-const 새로운 구문인 “상수 어설션(const assertions)“은 TypeScript 3.4에서 도입되었습니다. 이것은 변수가 변경되지 않을 것(immutable)이고 가능한 엄격한 타입을 제공해야 한다는 것을 타입스크립트에 알려줍니다. 이는 타입의 종류에 따라 다르게 영향을 미치므로, 이 글에서는 상수 어설션을 어떻게 사용하고 왜 유용한지에 대해 설명할 것입니다. 문자열/숫자 문자열이나 숫자에 “as const”를 추가하면 특정 값으로 타입을 좁힐 수 있습니다. 숫자의 경우 문자열 또는 숫자의 경우 일반적으로 “const”를 사용하여 변수를 정의하면 동일한 효과를 얻을 수 있기 때문에 덜 유용합니다. 런타임 안전성이라는 추가 이점도 있습니다. 때로는 변수로 값을 정의하지 않고, 단순히 문자열 리터럴을 사용하고 싶을 수 있습니다. 예를 들어 값을 반환할 때처럼요. 그때 “as const”…","fields":{"slug":"/typescript/typescript-as-const/"},"frontmatter":{"categories":"translate typescript","title":"(번역) 타입스크립트에서 'As Const' 이해하기","date":"April 14, 2024","draft":false}},"next":{"fields":{"slug":"/typescript/as-const-vs-satisfies/"}},"previous":{"fields":{"slug":"/react/process-env-destructuring-error.md/"}}},{"node":{"id":"863af574-c232-55ba-952a-d2a2e6490845","excerpt":"Argument of type ‘string’ is not assignable to parameter of type ‘“apple” | “banana”‘.(2345) 타입스크립트 사용시 때때로 예상치 못한 타입 호환성 문제에 직면하곤 합니다. 엄격한 타입 시스템을 가지고 있는 타입스크립트로 코드를 작성하다 보면 더욱 이러한 문제와 자주 직면합니다. 하지만 다행히도, 타입스크립트는 이러한 문제들을 해결할 수 있는 강력한 도구들을 제공합니다. 이 글에서는 타입 호환성 문제를 해결하는 데 있어 as const와 satisfies라는 두 가지 도구의 사용 방법과 각각의 장점에 대해 알아보겠습니다. 먼저 우리가 자주 마주할 수 있는 상황을 예시 코드로 살펴보겠습니다. 위에 에러 메시지는 함수의 인자로 예상되는 타입과 실제로 전달된 타입이 일치하지 않을 때 발생합니다. 이 경우, printPerson 함수는 \"apple\" 또는 \"banana\"라는 구체적인 문자열 타입을 기대하지만, perso…","fields":{"slug":"/typescript/as-const-vs-satisfies/"},"frontmatter":{"categories":"typescript","title":"타입스크립트 타입 호환성 문제 해결하기 \"as const vs satisfies\"","date":"March 31, 2024","draft":false}},"next":{"fields":{"slug":"/react/html-node-streaming/"}},"previous":{"fields":{"slug":"/typescript/typescript-as-const/"}}},{"node":{"id":"f5dbfa6e-986a-5edb-9bc0-2f30132ce702","excerpt":"원글: https://aralroca.com/blog/html-node-streaming 썸네일 최근 몇 년간 브라우저는 HTML과 자바스크립트를 스트리밍을 지원하기 시작했습니다. 이 글에서는 이에 대한 장점과 브라우저가 자동으로 수행하지 않는 다른 작업들을 통해 스트리밍의 이점을 최대한 활용할 수 있는 방법에 대해 이야기할 것입니다. 스트리밍 HTML 초기 로드하는 동안에는 브라우저가 자동으로 처리하기 때문에 크게 신경 쓸 필요가 없습니다. 스트리밍 하는 동안 HTML 청크를 받으면, 브라우저는 그 콘텐츠를 출력합니다. 서버에서 스트리밍을 활성화하려면 헤더를 조정해야 합니다. 아래는 예시입니다. 그리고 응답에서는 ReadableStream을 사용합니다. 이것은 Bun을 사용한 예시입니다. enqueue 안에 있는 각 문자열은 브라우저가 받게 될 청크입니다. 스트리밍 중 HTML 콘텐츠 변경 성능상의 이점이 많기 때문에 많이 사용되는 방법 중 하나는 스트리밍 되는 동안 HTML 콘…","fields":{"slug":"/react/html-node-streaming/"},"frontmatter":{"categories":"translate react","title":"(번역) 스트리밍 HTML과 DOM 비교 알고리즘","date":"March 21, 2024","draft":false}},"next":{"fields":{"slug":"/browser/browser-coordinate-size-api/"}},"previous":{"fields":{"slug":"/typescript/as-const-vs-satisfies/"}}},{"node":{"id":"e5b56e8b-108e-5455-8e4a-ebbfe2c18f09","excerpt":"프론트 개발을 하다보면 웹 페이지의 다양한 요소들을 직접 제어해야 하는 경우가 있습니다. 이 글에서는 CSSOM의 네 가지 주요 좌표 체계인 Offset, Page, Viewport, Screen에 대해 자세히 알아보고, 위치와 크기 관련 API는 어떤것이 있는지 살펴볼 예정입니다. 이 글을 통해 웹 페이지 내에서 요소의 위치와 크기를 정밀하게 제어하는 데 도움이 되길 바랍니다. 표준 CSSOM 좌표 체계 보통 클라이언트 개발에서 좌표의 원점은 왼쪽 하단이 아닌, 왼쪽 상단 입니다. CSS 객체 모델에서는 Offset, Page, Viewport, Screen 네 가지 표준 좌표 체계가 있습니다. 출처: MDN Coordinate_systems Offset 정의: 오프셋 좌표 체계는 특정 요소의 상대적 위치를 나타내는 데 사용됩니다. 이는 요소의 왼쪽 상단 모서리를 시작점(원점)으로 합니다. 특징: 이 좌표 체계는 요소의 부모 요소를 기준으로 한 상대적 위치를 제공합니다. 마…","fields":{"slug":"/browser/browser-coordinate-size-api/"},"frontmatter":{"categories":"browser","title":"브라우저 위치 및 크기 관련 API들","date":"March 03, 2024","draft":false}},"next":{"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}},"previous":{"fields":{"slug":"/react/html-node-streaming/"}}},{"node":{"id":"49246cb3-c4b8-5aeb-a4c5-a1b737e89b51","excerpt":"원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939 CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다. 이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다. 읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다. 약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리…","fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"},"frontmatter":{"categories":"translate a11y","title":"(번역) 접근성을 고려하여 CSS 작성하기","date":"February 18, 2024","draft":false}},"next":{"fields":{"slug":"/infra/docker-command/"}},"previous":{"fields":{"slug":"/browser/browser-coordinate-size-api/"}}},{"node":{"id":"4cbf47cf-4a24-5ffd-88c3-bdede11e8a72","excerpt":"Docker 엔진 시작하기/종료하기 도커 엔진: 자동 실행 설정 활성화시, 컴퓨터를 켰을 때 함께 자동으로 실행 Docker Desktop은 도커 엔진 자동으로 실행하도록 설정 되어있음. 리눅스 명령어로 엔진 시작, 종료, 자동 실행 설정 방법 systemctrl: 서비스 시작 및 중지 관련 리눅스 명령어 (system control) 컨테이너: 실행시킬 스크립트가 따로 필요. 컴퓨터를 켰을 때 함께 자동으로 실행 되지 않음. 컨테이너의 기본적인 사용 방법 컨테이너를 다루는 모든 명령어는 docker 명령어로 시작 도커 명령어 구성: docker 커맨드(무엇을 어떻게) 대상 + 옵션, 인자: docker 커맨드(무엇을 어떻게) 옵션 대상 인자 상위 커맨드는 생략 가능하다?! ex) docker run, docker start 커맨드(무엇을 어떻게)는 상위커맨드와 하위커맨드로 나뉘어짐. container 상위 커맨들를 생략하는 경우가 있음. 도커 1.13부터 커맨드가 재편되면서 …","fields":{"slug":"/infra/docker-command/"},"frontmatter":{"categories":"infra","title":"docker에서 자주쓰는 명령어","date":"February 18, 2024","draft":false}},"next":{"fields":{"slug":"/react/storybook-react-server-components/"}},"previous":{"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}}},{"node":{"id":"5fc01a99-7197-518c-a9de-60ace2976502","excerpt":"원문: https://storybook.js.org/blog/storybook-react-server-components/ 스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용 리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다. 가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다. 이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 …","fields":{"slug":"/react/storybook-react-server-components/"},"frontmatter":{"categories":"translate react","title":"(번역) 리액트 서버 컴포넌트를 위한 스토리북","date":"February 02, 2024","draft":false}},"next":{"fields":{"slug":"/react/next-app-router-react-query/"}},"previous":{"fields":{"slug":"/infra/docker-command/"}}},{"node":{"id":"161feee4-18f2-5161-8900-a9bc5eda4367","excerpt":"지난 글에서 react-query의 hydrate, dehydrate을 통해 서버에서 prefetching 한 데이터 사용하는 방법에 대해서 살펴보았습니다. 서버에서 prefetching 한 데이터 사용하기 오늘은 조금 실용적으로 Next.js 13, 14 버전의 app router에서 react-query를 어떻게 사용하고 세팅하면 좋을지 고민했던 내용에 대해서 이야기해보도록 하겠습니다. 해당 글은 23년 10월에 메이저 버전 업데이트된, Next.js 14와 React-Query 5 를 기준으로 작성된 글입니다. 예시로 보여주는 코드는 저의 next-14-react-query repo에서 확인 할 수 있습니다. 제가 크게 고민했던 문제는 아래 세 가지입니다. Hydrate vs ReactQueryStreamedHydration 두 가지 API 중에 어떤 것을 채택할까? Hydration API 사용 시에 RSC, RCC 모두에서 깔끔한 코드를 유지하려면 queryOption은 …","fields":{"slug":"/react/next-app-router-react-query/"},"frontmatter":{"categories":"react react-query","title":"Next.js app router에서 React Query 사용하면서 고민했던 것들","date":"January 07, 2024","draft":false}},"next":{"fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"}},"previous":{"fields":{"slug":"/react/storybook-react-server-components/"}}},{"node":{"id":"55331269-8b15-516d-aa6d-7eb90d2fedee","excerpt":"원문: https://javascript.plainenglish.io/the-5-most-innovative-proposals-for-javascript-planned-for-2024-22139dd2f546 JavaScript Logo 2024 자바스크립트가 빠른 속도로 발전함에 따라 2024년은 이 프로그래밍 언어에 있어 중요한 해가 될 것입니다. 이 글에서는 개발자들이 자바스크립트 코딩에 접근하는 방식을 재정의하기 위한 최신 제안을 자세히 살펴보겠습니다. 이 글이 유용하길 바랍니다! 시작해 봅시다! 목차 데코레이터 Temporal API 파이프라인 연산자 Error cause 레코드와 튜플 1. 데코레이터 자바스크립트에 데코레이터가 도입된 것은 개발자가 클래스, 메서드, 프로퍼티 및 매개변수의 동작을 조작하고 개선할 수 있는 방법이 크게 발전했음을 의미합니다. 파이썬과 타입스크립트와 같은 다른 프로그래밍 언어에서 유래한 데코레이터는 기능을 추가하거나 수정하는 간결하고 선언적인…","fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"},"frontmatter":{"categories":"translate javascript","title":"(번역) 2024년 자바스크립트의 5가지 가장 혁신적인 제안","date":"December 20, 2023","draft":false}},"next":{"fields":{"slug":"/react/server-rendering-and-react-query/"}},"previous":{"fields":{"slug":"/react/next-app-router-react-query/"}}},{"node":{"id":"d77b67b4-3ea0-53b0-b513-8a24dd4da08d","excerpt":"Next.js나 Remix 같은 프레임워크 내에서 React-Query를 사용한다면, 서버 렌더링 될 때 요청 후 응답받은 데이터를 SPA 방식으로 전환되고 나서도 유지할 수 있을까요? 어떻게 가능할까요? React Query의 hydrate와 dehydrate는 서버에서 미리 가져온 데이터를 클라이언트 사이드에서 재사용 할 수 있게 해줍니다. 이번 글을 통해 서버 렌더링과 어떻게 이를 가능하게 하는지 hydrate와 dehydrate에 대해서 알아봅시다. Server Rendering 서버 렌더링은 사용자가 페이지를 로드하는 즉시 볼 수 있는 초기 HTML을 서버에서 생성하는 행위입니다. 이는 페이지 요청 시 즉시 발생할 수 있으며(SSR), 이전 요청이 캐시 되었거나 빌드 시간에 미리 생성(SSG) 할 수도 있습니다. 클라이언트 렌더링 애플리케이션에서는 사용자에게 화면에 콘텐츠를 표시하기 전에 최소 3번의 서버 왕복(roundtrips)이 필요합니다. 서버 렌더링은 위의 과정…","fields":{"slug":"/react/server-rendering-and-react-query/"},"frontmatter":{"categories":"react react-query","title":"서버에서 React Query prefetching 한 데이터 사용하기","date":"December 10, 2023","draft":false}},"next":{"fields":{"slug":"/react/introducing-signals/"}},"previous":{"fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"}}},{"node":{"id":"9c6b2391-e371-545b-b36b-85a35b38a7ac","excerpt":"원문: https://preactjs.com/blog/introducing-signals/ 시그널은 앱이 복잡해져도 빠른 속도를 유지하도록 하는 상태 표현 방식입니다. 시그널은 반응형 원칙에 기반을 두고 있으며, 가상 돔에 최적화된 독특한 구현을 통해 개발자에게 훌륭한 경험을 제공합니다. 본질적으로 시그널은 특정 값을 가지고 있는 .value 속성을 가진 객체입니다. 컴포넌트 내에서 시그널의 value 속성에 접근하면, 그 시그널의 값이 변경될 때 해당 컴포넌트가 자동으로 업데이트됩니다. 이는 간단하고 작성하기 쉬울 뿐만 아니라, 앱이 얼마나 많은 컴포넌트를 가지고 있든 상태 업데이트가 빠르게 유지되도록 보장합니다. 시그널은 기본적으로 빠르며, 백그라운드에서 자동으로 업데이트를 최적화해줍니다. REPL에서 실행 시그널은 훅과 달리 컴포넌트 내부 또는 외부에서 사용할 수 있습니다. 또한 시그널은 훅과 클래스 컴포넌트 모두에서 훌륭하게 작동하므로, 기존 지식을 활용하며 자신의 속도에 …","fields":{"slug":"/react/introducing-signals/"},"frontmatter":{"categories":"translate react","title":"(번역) 시그널(Signal)에 대한 소개","date":"November 27, 2023","draft":false}},"next":{"fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"}},"previous":{"fields":{"slug":"/react/server-rendering-and-react-query/"}}},{"node":{"id":"5f49b3c9-9991-5b3e-9e49-46ec9a85c02d","excerpt":"원글: https://itnext.io/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components-82e46b5820c 프런트엔드 개발 영역에서는 용어와 패러다임이 때로는 이해하기 어려울 수 있으며 ‘헤드리스 UI’ 또는 ‘헤드리스 컴포넌트’도 이 범주에 속할 수 있습니다. 이러한 용어들이 무엇을 의미하는지 궁금해서 고개를 갸웃거리고, 혼자만 그런 것이 아닙니다. 사실, 혼란스러운 이름에도 불구하고 이러한 개념들은 복잡한 사용자 인터페이스 관리를 상당히 단순화할 수 있는 매력적인 전략입니다. 헤드리스 컴포넌트는 난해해 보일 수 있지만, 그 진정한 힘은 유연성, 재사용 가능성, 그리고 코드베이스의 구성과 깔끔함을 향상시킬 수 있는 능력에 있습니다. 이 글에서는 이 패턴이 정확히 무엇인지, 왜 유용한지, 그리고 인터페이스 디자인에 대한 접근 방식을 어떻게 혁신할 수 있는지에 대해 탐구해 볼 것입니다. …","fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"},"frontmatter":{"categories":"translate react","title":"(번역) React에서 UI와 로직 분리하기:헤드리스 컴포넌트를 사용한 클린 코드 접근법","date":"September 13, 2023","draft":false}},"next":{"fields":{"slug":"/cs/design-patterns-for-humans/"}},"previous":{"fields":{"slug":"/react/introducing-signals/"}}},{"node":{"id":"44fd3704-304a-5b2a-8702-af196e247d28","excerpt":"원글: https://github.com/kamranahmedse/design-patterns-for-humans/blob/master/readme.md 역자주: 이 글은 Kamran Ahmed의 글이며, PHP-7 기준으로 작성된 예제 코드를 타입스크립트로 변경하였습니다. logo 🎉 디자인 패턴에 대한 초간단 설명입니다! 🎉 누구든지 혼란스럽게 만들 수 있는 주제입니다. 이 글에서는 최대한 간단한 방식으로 설명함으로써 당신의 기억 속 (그리고 아마도 제 기억)에 각인시키기 위해 노력할 것입니다. 저의 다른 프로젝트도 확인하고 트위터에서 “안녕”이라고 인사해 보세요. 생성 패턴 구조 패턴 행동 패턴 심플 팩토리(Simple Factory) 어댑터(Adapter) 책임 연쇄(Chain of Responsibility) 팩토리 메서드(Factory Method) 브릿지(Bridge) 명령(Command) 추상 팩토리(Abstract Factory) 컴포지트(Composite) 반복자…","fields":{"slug":"/cs/design-patterns-for-humans/"},"frontmatter":{"categories":"translate cs","title":"(번역) 우리들을 위한 디자인 패턴","date":"August 27, 2023","draft":false}},"next":{"fields":{"slug":"/javascript/garbage-collection/"}},"previous":{"fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"}}},{"node":{"id":"7e56bfba-a382-5266-96c6-f581980fe75d","excerpt":"힙과 스택 메모리의 차이를 살펴보고, 자바스크립트 대표 엔진인 V8의 Resident set 구조와 Minor GC, Major GC 의 동작 알고리즘에 대해서 자세하게 살펴 보도록 하겠습니다. 이 글이 가비지 컬렉터가 어떻게 동작하는지 이해하는데 도움이 되길 바랍니다. V8 메모리 구조 (feat. Resident set) Resident Set은 실행 중인 프로그램의 메모리 관리를 위해 사용되는 용어로, 현재 V8 프로세스가 사용하는 모든 메모리를 나타냅니다. 이것은 JavaScript 코드, 객체, 함수, 변수 등을 저장하는 데 필요한 모든 메모리를 포함합니다. Resident Set은 다음과 같이 여러 세그먼트로 구성됩니다. \n출처: memory management in V8 stack vs heap 메모리 공간은 크게 스택 메모리(Stack memory)와 힙 메모리(Heap memory)로 구분되며, 힙 메모리는 다양한 목적으로 서비스하는 여러 공간으로 더 나누어집니다.…","fields":{"slug":"/javascript/garbage-collection/"},"frontmatter":{"categories":"javascript","title":"자바스크립트 가비지 컬렉션 알고리즘","date":"July 14, 2023","draft":false}},"next":{"fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"}},"previous":{"fields":{"slug":"/cs/design-patterns-for-humans/"}}},{"node":{"id":"1a5d4b16-06aa-572e-802e-98a3de358758","excerpt":"원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …","fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"},"frontmatter":{"categories":"translate react react-query","title":"(번역) React Query를 사용하여 서버 상태를 관리하는 방법","date":"June 21, 2023","draft":false}},"next":{"fields":{"slug":"/cs/6-caching-strategies/"}},"previous":{"fields":{"slug":"/javascript/garbage-collection/"}}},{"node":{"id":"872218ff-8507-5910-8001-bc52d72fca27","excerpt":"원글: https://javascript.plainenglish.io/6-caching-strategies-to-remember-while-designing-cache-system-da058a3757cf 캐시 시스템 관련 용어, 읽기 vs 쓰기 중심의 애플리케이션에서 캐싱 전략, 캐시를 무효화하는 방법 등에 대해서 알려드리겠습니다. 개요 캐싱의 목표는 원본 소스에서 데이터를 가져오는 횟수를 줄여 처리 속도를 높이고 대기 시간을 감소시키는 것입니다. 캐싱은 인메모리 캐싱, 디스크 캐싱, 데이터베이스 캐싱, CDN 캐싱과 같은 다양한 수준의 아키텍처에서 구현될 수 있습니다. 데이터는 각각 고유한 이점이 있는 다양한 기술을 사용하여 캐싱할 수 있습니다. 인메모리 캐싱은 컴퓨터의 주 메모리에 데이터를 저장하여 디스크 저장소 보다 빠른 액세스를 제공합니다. 반면 디스크 캐싱은 하드 디스크에 데이터를 저장하므로 주 메모리보다는 느리지만 원격 소스에서 데이터를 가져오는 것보다 빠릅니다. 데이터…","fields":{"slug":"/cs/6-caching-strategies/"},"frontmatter":{"categories":"translate cs","title":"(번역) 캐시 시스템 설계할 때 기억해야 할 6가지 캐싱 전략","date":"June 13, 2023","draft":false}},"next":{"fields":{"slug":"/javascript/metaprogramming/"}},"previous":{"fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"}}},{"node":{"id":"7268c9ab-b8e9-5638-aa7a-c6c2e2d4f1a8","excerpt":"ES6에서는 Proxy, Reflect 객체를 통해 메타프로그래밍을 할 수 있습니다. 메타프로그래밍이 무엇인지? 그리고 Proxy와 Reflect에 대해서 알아보도록 하겠습니다. Metaprogramming 메타프로그래밍은 프로그램이 자기 자신을 조작할 수 있는 능력을 말합니다. 이를 통해 프로그램은 동적으로 자기 자신을 변화시키거나, 다른 프로그램을 분석하거나, 새로운 코드를 생성할 수 있습니다. 메타프로그래밍을 통해 코드의 유연성과 재사용성을 높일 수 있으며, 런타임에서 객체의 동작을 수정할 수 있어서 더욱 강력한 코드를 작성할 수 있습니다. 메타프로그래밍에서 가장 중요한 세 가지 개념 Introspection Introspection은 코드가 자기 자신을 검사할 수 있는 능력을 의미합니다. 이를 통해 코드의 내부 구조와 데이터를 검사하거나, 코드의 실행 시점에서 객체의 메소드나 프로퍼티에 접근할 수 있습니다. 자바스크립트에서는 ES6부터 Reflect 객체를 제공하여 Intr…","fields":{"slug":"/javascript/metaprogramming/"},"frontmatter":{"categories":"javascript","title":"Proxy, Reflect와 메타프로그래밍","date":"May 07, 2023","draft":false}},"next":{"fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"}},"previous":{"fields":{"slug":"/cs/6-caching-strategies/"}}},{"node":{"id":"09bde05f-002e-5468-8934-6141fab54678","excerpt":"원문: https://blog.openreplay.com/deep-copying-objects-with-the-structuredclone-api/ Deep Copying Objects with the StructuredClone API 개요: 자바스크립트에서 객체를 복사하는 것은 간단하지 않으며, 이는 잘 알려진 문제입니다. 그러나 이 글에서는 해결책을 제공합니다. StructuredClone API를 사용하면 모든 객체를 간단하고 빠르게 복사할 수 있습니다. 자바스크립트에서 객체가 변수에 저장될 때, 해당 변수는 객체의 참조값을 갖습니다. 이는 변수 자체에 객체를 저장하는 것이 아니라, 객체의 메모리 위치를 나타내는 식별자를 저장한다는 것을 의미합니다. 객체의 복사는 원시 타입과는 다른 방식으로 동작합니다. 얕은 복사(Shallow Copy) vs. 깊은 복사(Deep Copy) 자바스크립트에서 값은 두 가지 방법으로 복사할 수 있습니다. 얕은 복사와 깊은 복사입니다. 얕은 복…","fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"},"frontmatter":{"categories":"translate javascript","title":"(번역) StructuredClone API를 사용하여 객체를 깊은 복사하는 법","date":"May 07, 2023","draft":false}},"next":{"fields":{"slug":"/feature/input-date/"}},"previous":{"fields":{"slug":"/javascript/metaprogramming/"}}},{"node":{"id":"7dd77d91-4c83-5f76-92b0-9e9b60089ea7","excerpt":"Date와 인연이 깊은지 날짜 선택 UI를 custom 해서 개발해야 하는 경험을 두 번째 하게 되었다. mui datepicker를 붙이기엔 너무 과해서 react-calendar 라이브러리를 사용하려고 했는데, 문제는 input은 함께 딸려오지 않기 때문에 input 관련 interaction은 직접 구현해야 했다. 인 하우스용 서비스라 여러 라이브러리 참고해서 직접 인터렉션을 정의하고 구현해 보았는데 기록 차 경험을 남겨본다! TL;DR; 전체 구현 결과물은 code sandbox에서 확인할 수 있다. DatePicker 컴포넌트를 개발해 보자! (calendar는 라이브러리를 사용) 딱히 input에 대한 큰 요구사항이 없이 기능 개발을 해야 하는 경우 그냥 react-date-picker를 써라 DatePicker 컴포넌트 개발 dependency react-calendar dayjs props 설계 minDate, maxDate: date range를 지원해야하므로 …","fields":{"slug":"/feature/input-date/"},"frontmatter":{"categories":"feature","title":"날짜 입력 input 인터렉션 개발기","date":"April 23, 2023","draft":false}},"next":{"fields":{"slug":"/react/recoil-introduction/"}},"previous":{"fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"}}},{"node":{"id":"08e40669-d8eb-531c-ab48-fcad02ba39ef","excerpt":"React 18로 버전업 하면서 프로젝트에 Recoil을 도입했는데, 그 과정에서 고민했던 내용을 기록차 남겨본다.\n TL;DR; 개념 정리나 장단점 비교에 대해서는 이미 알고 계신분들도 있을 것 같아서 폴더구조나 Redux를 대체하는게 맞는가에 대해서만 먼저 간단하게 요약한 내용을 공유한다. 1. 폴더 구조는 data source 별로 나누자 Suggested folder structure for atoms and selectors recoil 폴더 하위에 data source 별로 나누자 data source 하위에 atoms, selectors, hooks를 두자 2. concurrency mode 사용하려면 Redux는 버리고 Recoil로 넘어가야하나? useSyncExternalStore hook 을 사용해서 Redux에서도 동시성 모드를 사용할 수 있다. 이 부분에 대해서 고민해 본 내용은 아래에.. Recoil 도입 배경 Recoil은 Facebook에서 개발한 상태…","fields":{"slug":"/react/recoil-introduction/"},"frontmatter":{"categories":"react","title":"Recoil 도입기(feat. 폴더구조)","date":"April 08, 2023","draft":false}},"next":{"fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"}},"previous":{"fields":{"slug":"/feature/input-date/"}}},{"node":{"id":"e19abd3b-8537-528d-a31e-3a74ebbb5cc5","excerpt":"Introduction #1077 이슈를 보면 mutateFn의 variable을 필수값으로 변경한 이력을 볼 수 있다. mutation인데 왜 variable이 없냐고? 나의 경우에는 URL의 있는 queryParams를 읽어와서 데이터를 넘겨주면 되기 때문에 useSomething hook 안에서 처리하고 싶었다. 문제상황 mutate() 와 같이 variable에 아무 값도 넣지 않고 mutation 함수를 호출 할 수 없다.\nTVariables 타입에 undefined도 줘보고 이래저래 별짓을 다해봐도 필수값을 optional 하게 바꿀수는 없었다. 에러 발생\n 해결 실질적으로는 mute 함수에서 variable을 정의해서 넣어주는 것이기 때문에 그렇게 꼼수해결법도 아니다.","fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"},"frontmatter":{"categories":"react react-query troubleshooting","title":"React Query useMutation에서 variable 옵셔널하게 사용하기","date":"February 15, 2023","draft":false}},"next":{"fields":{"slug":"/react/react-query-basic/"}},"previous":{"fields":{"slug":"/react/recoil-introduction/"}}},{"node":{"id":"e1211b4f-4197-5605-879f-45d011d0d210","excerpt":"Introduction 이 글을 읽기전 React의 상태관리 종류 4가지를 먼저 읽으면 이해가 쉬울 수 있다. 얼마전 회사 프로젝트에 react-query를 도입하여 사용중인데 정말 너무너무 좋다. react-query를 이용해서 server state를 분리하다보니 loading 처리나, refetch 등 불필요한 코드가 많이 줄었다. 뭐든지 처음이 어렵지 큰 그림을 알고있으면 도입하여 적용하는데에는 큰 문제가 없고, 사용하면서 상황에 맞게 옵션과 아키텍쳐를 변경하면 된다고 생각하기 때문에, 이번 포스팅에서는 react-query를 적용하기 위해 필요한 전반적인 개념을 정리해보고자 한다. 용도 react-query는 서버 데이터 관리를 위한 라이브러리다. 이 라이브러리는 API 호출, 캐시 및 상태 관리, 오류 처리 등의 작업을 간단하게 처리할 수 있도록 도와준다. 서버 데이터 관리는 중요한 부분 중 하나다. 클라이언트에서는 서버로부터 데이터를 가져와(Read) 화면에 그려주고…","fields":{"slug":"/react/react-query-basic/"},"frontmatter":{"categories":"react react-query","title":"React Query 시작하기 (feat. Tanstack)","date":"February 05, 2023","draft":false}},"next":{"fields":{"slug":"/react/react-state-management/"}},"previous":{"fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"}}},{"node":{"id":"afca1d77-1bf9-59de-8227-74dfb0b19eed","excerpt":"Introduction react의 상태관리 종류 4가지를 알아보고, 각각의 종류별로 왜 다르게 관리해야하는지와 React에서 제공하는 API 혹은 어떤 라이브러리를 사용해서 관리하면 좋을지 알아보도록 하자. 상태관리 종류 Local state Global state App 어느곳(여러 컴포넌트)에서 state를 확인하거나 업데이트하기 위해서 필요함. 예: authenticated user state - logged in/out Server state 서버로 받은 데이터가 UI 상태와 통합되어야 하는 경우 필요함. 예: loading, error… 대표 라이브러리: SWR, react-query URL state URL에 존재하는 데이터(pathname, query params..) 1) Local state useState useReducer useReducer vs useState 차이 useState는 하나의 state operation만 가능했다면, useReducer는 여…","fields":{"slug":"/react/react-state-management/"},"frontmatter":{"categories":"react","title":"React의 상태관리 종류 4가지","date":"February 03, 2023","draft":false}},"next":{"fields":{"slug":"/react/error-handle/"}},"previous":{"fields":{"slug":"/react/react-query-basic/"}}},{"node":{"id":"0e971c95-1f7f-53c0-b77f-9ed5af3af6e0","excerpt":"고민 혹시 제가 내린 결론에 다른 의견을 주신다면 매우 감사합니다. 🙇🏻‍♀️ 에러를 발생 vs 사전 확인을 통해 예외 처리 어느 부분까지 사전 확인이 필요한 걸까? 고민하게 된 배경 함수의 매개변수가 의도한 것과 다른 값이 들어왔을 때 에러가 발생할 수 있다. 그런데 문득 에러를 발생하게 하는 게 올바른 것인가? 아니면 애초에 에러가 발생할 상황을 하나도 빠짐없이 막는 것이 올바른 것인가? 고민이 들었다. 위의 코드를 보면 event 객체가 무조건 존재할 상황을 가졍하고 있으므로 만약 비정상적인 event 객체가 첫 번째 인자로 전달된다면 에러가 발생할 것이다. 그렇다면 이렇게 코드를 짜는 것이 좋을까? 애초에 event 객체가 존재하지 않을 수 있는 케이스를 고려해서 절대로 에러를 발생하지 않게 하는 것이 좋을까? 결론 App을 Crashed 시키지 않는 한해서 에러가 발생해야 하는 경우는 발생하게 내버려 두고, 예상할 수 있는 예외는 try…catch를 이용하여 에러가 전파되지 않도…","fields":{"slug":"/react/error-handle/"},"frontmatter":{"categories":"react","title":"에러 핸들링에 대한 고민 (feat. React)","date":"October 16, 2022","draft":false}},"next":{"fields":{"slug":"/browser/touch-mouse-event/"}},"previous":{"fields":{"slug":"/react/react-state-management/"}}},{"node":{"id":"47a3c1ed-8f39-56be-81c1-c0cd470b4c41","excerpt":"문제상황 dropdown 컴포넌트를 만들었는데, focus가 다른곳으로 이동하면 펼쳐져있던 dropdown이 접혀져야 해서 blur 이벤트 발생시 접히도록 했다. 그랬더니 펼쳐져 있는 항목 중 하나를 click 했을때, 위에서 설정한 blur 이벤트가 먼저 발생하면서 click 이벤트가 호출되지 않는 이슈가 문제였다. dropdown_비정상동작.gif 문제의 코드 TL;DR Touch, mouse 이벤트 순서 touchstart touchmove touchend mouseover mousemove mousedown mouseup click 해결방법 click시 처리해줘야 하는 이벤트 핸들러를 click 이 아닌 mousedown 에서 처리해준다. dropdown_정상동작.gif Dropdown CodeSandbox Touch and mouse 이벤트 그러면 지금부터는 web.dev 문서를 살펴보면서 알게된 touch 이벤트의 도입 배경과 touch, mouse 이벤트를 함께 다루기…","fields":{"slug":"/browser/touch-mouse-event/"},"frontmatter":{"categories":"feature browser","title":"Touch, Mouse 이벤트 함께 다루기 (feat. dropdown)","date":"September 18, 2022","draft":false}},"next":{"fields":{"slug":"/infra/jenkins-bitbucket/"}},"previous":{"fields":{"slug":"/react/error-handle/"}}},{"node":{"id":"bacf9614-be80-5449-8b45-fa90310ef8dd","excerpt":"목표 Jenkins pipeline을 이용하여, bitbucket에서 PR 생성시 Jenkins job을 실행시키고 그 결과를 slack으로 전송한다. (Jenkins 내의 credential 설정이나 자세한 Jenkins 플러그인 설정 사항은 해당 글에서 포함하지 않음.) Webhook 웹훅이란 뭘까? 위의 목표를 이루려면 우리는 웹훅이 필요하다. 어플리케이션에 한정하여 특정 이벤트가 발생할 때 지정된 URL로 push 알림을 보내는 것이 webhook이다. callback 이랑 비슷한데, 조금 더 작은 범위라고 생각하면 된다. 이 방법은 주기적으로 검사하여 변경 사항이 있는지 확인하는 것보다 훨씬 효율적이다. 구현 방법 우리는 bitbucket에서 PR이 생성되었을때 Jenkins 플러그인인 Generic Webhook Trigger를 호출 하고, Jenkins Job 실행이 끝났을 때 slack Webhook을 호출 할 것이다. 1. Jekins에서 Generic Webho…","fields":{"slug":"/infra/jenkins-bitbucket/"},"frontmatter":{"categories":"infra","title":"Bitbucket과 Jenkins를 연동하여 테스트 자동화 (feat. Generic Webhook Trigger)","date":"September 04, 2022","draft":false}},"next":{"fields":{"slug":"/feature/confirm/"}},"previous":{"fields":{"slug":"/browser/touch-mouse-event/"}}},{"node":{"id":"2b300262-3404-5526-8a92-b3c887f4e178","excerpt":"보통 어플리케이션에서 공통된 디자인의 confirm 창을 사용한다. 이때, window.confirm과 같이 고객의 OK/CANCEL 클릭 여부에 따라서 다음 동작을 이어나가게 하고싶다면 어떻게 구현해야할까? 준비물 Modal Component (global 하게 등록) Global State Modal Hook 보통 웹 어플리케이션에서는 공통된 디자인의 confirm을 사용하므로 화면에 보여지는 역할을 하는 Modal Component가 필요하다. 이 컴포넌트는 어플리케이션 전반에 걸쳐서 자주 사용하는 라이브러리이므로 App.js에서 global 하게 로드한다. global 하게 등록된 Modal 컴포넌트를 제어하기 위해서는 어플리케이션 어느 곳에서든 제어 가능하게 하기 위해서 global state가 필요하다. 그래서 modal 관련 state가 변경되었을때, Modal 컴포넌트가 render 되게 한다. 이제 window.confirm 처럼 사용하기 위해 confirm 함수를…","fields":{"slug":"/feature/confirm/"},"frontmatter":{"categories":"feature","title":"Promise를 사용하여 window.confirm 구현하기","date":"August 19, 2022","draft":false}},"next":{"fields":{"slug":"/test/test-introduction/"}},"previous":{"fields":{"slug":"/infra/jenkins-bitbucket/"}}},{"node":{"id":"34e53d64-0467-56f3-8179-68e3f4d6c48c","excerpt":"🔨 다듬을 필요가 있는 글입니다. 테스트 flow 테스트 코드 작성 → 테스트 읽고 → 실행 → 출력(Reporter) 테스트 종류 단위 테스트: 모듈을 분리된 환경에서 테스트 의존성이 있는 모듈을 제어하기 위해 필연적으로 모의 객체(Mocking)을 사용할 수밖에 없으며, 이 경우 각 모듈이 실제로 잘 연결되어 상호 작용하는지에 대해서는 검증하지 못한다. 통합 테스트: 2개 이상의 모듈 간의 상호작용을 테스트 (개발자 관점의 테스트) 단위 테스트에 비해 mocking을 덜하며, 모듈 간의 연결에서 발생하는 에러를 검증할 수 있다. E2E 테스트: 사용자의 실행 환경과 거의 동일한 환경에서 테스트를 진행 (사용자 관점의 테스트) 세부 모듈들이 갖는 다양한 상황들의 조합을 고려해야 하기 때문에 테스트 자바스크립트 테스트 도구 자바스크립트 테스트는 도구들은 아래와 같이 분류할 수 있다. Test Runners Testing Frameworks Assertion Libraries Tes…","fields":{"slug":"/test/test-introduction/"},"frontmatter":{"categories":"test","title":"자바스크립트 테스트 개념정리","date":"April 14, 2022","draft":false}},"next":null,"previous":{"fields":{"slug":"/feature/confirm/"}}}],"categories":["All","framework","react","next","translate","typescript","browser","a11y","infra","react-query","javascript","cs","feature","troubleshooting","test"]}}, + "result": {"pageContext":{"currentCategory":"All","edges":[{"node":{"id":"ee4fa349-0d3c-533a-9bcf-38d5b6c48497","excerpt":"TL;DR; TypeScript와 ESLint는 “의도적으로 무시”하는 코드에 대해 다른 접근 방식을 가집니다. TypeScript는 언더스코어(_) 접두사로 시작하는 변수를 자동으로 무시하지만, ESLint의 no-unused-vars 규칙은 기본적으로 더 엄격하므로, 언더스코어로 시작하는 변수도 사용되지 않은 것으로 간주하여 오류를 발생시킵니다. 개발자는 ESLint 설정을 통해 “의도적으로 무시”하는 코드에 대해 TypeScript와 유사한 동작을 구현하거나 더 세밀한 제어를 할 수 있습니다. “의도적으로 무시”하는 코드 아래 예시와 같은 코드를 작성해본 경험이 있으신가요? 우리는 가끔 “의도적으로 무시” 하는 것으로 간주하는 코드를 작성해야 할 때가 있습니다. 루프 인덱스 무시: _index는 사용되지 않지만, entries() 메서드의 반환 값을 구조 분해하기 위해 필요 콜백 함수에서 매개변수 무시: _item은 사용되지 않지만, index에 접근하기 위해 선언 구조 분…","fields":{"slug":"/typescript-eslint-handling-intentionally-ignored-variables/"},"frontmatter":{"categories":"framework","title":"‘의도적으로 무시’하는 코드에 대한 TypeScript와 ESLint의 다른 처리 방식","date":"October 26, 2024","draft":false}},"next":{"fields":{"slug":"/astro-hydration/"}},"previous":null},{"node":{"id":"87e0c0f2-6c03-513a-99a6-ef53d6389653","excerpt":"Astro는 애플리케이션을 구축이 아닌 콘텐츠에 중점을 두고 설계된 정적 사이트 빌더입니다. 따라서 블로그, 포트폴리오, 이벤트용 마케팅 성격의 사이트 개발에 매우 적합한 프레임워크 입니다. Astro 컴포넌트에 대해 먼저 간략히 알아보고, 어떻게 여러 프레임워크(React, Preact,Svelte, Vue, SolidJS)를 하나의 프로젝트에서 혼합해서 사용할 수 있는지 알아보도록 하겠습니다. Astro 컴포넌트 Astro 컴포넌트는 모든 Astro 프로젝트의 기본 구성 요소입니다. 이는 클라이언트 측 런타임이 없는 HTML 전용 템플릿 컴포넌트로 .astro 파일 확장자 사용합니다. Astro 컴포넌트에 대해 알아야 할 가장 중요한 점은 클라이언트에서 렌더링되지 않는다는 것입니다. HTML로 렌더링되며, 컴포넌트 스크립트는 브라우저로 전송되는 최종 페이지에서 모두 제거됩니다. 구성(스크립트 + 템플릿) .astro 파일은 스크립트와 템플릿으로 구성됩니다. src/compone…","fields":{"slug":"/astro-hydration/"},"frontmatter":{"categories":"framework","title":"Astro로 알아보는 Selective & Progressive Hydration","date":"October 13, 2024","draft":false}},"next":{"fields":{"slug":"/react/process-env-destructuring-error.md/"}},"previous":{"fields":{"slug":"/typescript-eslint-handling-intentionally-ignored-variables/"}}},{"node":{"id":"909b8521-890b-5dc7-9c04-3289e2502910","excerpt":"process.env 값을 구조 분해 할당 하셨나요? 우리가 환경변수를 접근할 때 객체의 속성값에 접근할 때 사용하는 점 표기법을 사용하기 때문에 구조 분해 할당을 자연스럽게 사용해도 될 것 같은데요. 이런 경우, 왜 에러가 발생하는지 이번 글을 통해 알아보겠습니다. 문제 상황 다음과 같은 코드가 있다고 가정해 봅시다. 위 코드는 process.env 객체에서 API_KEY와 NEXT_PUBLIC_ANALYTICS_ID 변수를 구조 분해 할당하여 사용하려고 합니다. 그러나 Next.js에서는 이 코드가 제대로 작동하지 않을 수 있고, 오류가 발생할 수 있습니다. 왜 이런 문제가 발생할까요? process.env는 일반적인 자바스크립트 객체와는 다르기 때문입니다. 이는 Node.js 환경에서 환경 변수를 담고 있는 특수한 객체로, 모든 속성이 문자열 형태로 저장됩니다. Next.js는 빌드 타임과 런타임에서 환경 변수에 접근하는 방식이 다르므로, 이로 인해 구조 분해 할당을 사용할 …","fields":{"slug":"/react/process-env-destructuring-error.md/"},"frontmatter":{"categories":"react next","title":"환경 변수(process.env)를 구조 분해 할당하면 안되는 이유","date":"June 26, 2024","draft":false}},"next":{"fields":{"slug":"/typescript/typescript-as-const/"}},"previous":{"fields":{"slug":"/astro-hydration/"}}},{"node":{"id":"7af1f27b-4f32-56a8-8140-5191c8ba3150","excerpt":"원글: https://www.omarileon.me/blog/typescript-as-const 새로운 구문인 “상수 어설션(const assertions)“은 TypeScript 3.4에서 도입되었습니다. 이것은 변수가 변경되지 않을 것(immutable)이고 가능한 엄격한 타입을 제공해야 한다는 것을 타입스크립트에 알려줍니다. 이는 타입의 종류에 따라 다르게 영향을 미치므로, 이 글에서는 상수 어설션을 어떻게 사용하고 왜 유용한지에 대해 설명할 것입니다. 문자열/숫자 문자열이나 숫자에 “as const”를 추가하면 특정 값으로 타입을 좁힐 수 있습니다. 숫자의 경우 문자열 또는 숫자의 경우 일반적으로 “const”를 사용하여 변수를 정의하면 동일한 효과를 얻을 수 있기 때문에 덜 유용합니다. 런타임 안전성이라는 추가 이점도 있습니다. 때로는 변수로 값을 정의하지 않고, 단순히 문자열 리터럴을 사용하고 싶을 수 있습니다. 예를 들어 값을 반환할 때처럼요. 그때 “as const”…","fields":{"slug":"/typescript/typescript-as-const/"},"frontmatter":{"categories":"translate typescript","title":"(번역) 타입스크립트에서 'As Const' 이해하기","date":"April 14, 2024","draft":false}},"next":{"fields":{"slug":"/typescript/as-const-vs-satisfies/"}},"previous":{"fields":{"slug":"/react/process-env-destructuring-error.md/"}}},{"node":{"id":"863af574-c232-55ba-952a-d2a2e6490845","excerpt":"Argument of type ‘string’ is not assignable to parameter of type ‘“apple” | “banana”‘.(2345) 타입스크립트 사용시 때때로 예상치 못한 타입 호환성 문제에 직면하곤 합니다. 엄격한 타입 시스템을 가지고 있는 타입스크립트로 코드를 작성하다 보면 더욱 이러한 문제와 자주 직면합니다. 하지만 다행히도, 타입스크립트는 이러한 문제들을 해결할 수 있는 강력한 도구들을 제공합니다. 이 글에서는 타입 호환성 문제를 해결하는 데 있어 as const와 satisfies라는 두 가지 도구의 사용 방법과 각각의 장점에 대해 알아보겠습니다. 먼저 우리가 자주 마주할 수 있는 상황을 예시 코드로 살펴보겠습니다. 위에 에러 메시지는 함수의 인자로 예상되는 타입과 실제로 전달된 타입이 일치하지 않을 때 발생합니다. 이 경우, printPerson 함수는 \"apple\" 또는 \"banana\"라는 구체적인 문자열 타입을 기대하지만, perso…","fields":{"slug":"/typescript/as-const-vs-satisfies/"},"frontmatter":{"categories":"typescript","title":"타입스크립트 타입 호환성 문제 해결하기 \"as const vs satisfies\"","date":"March 31, 2024","draft":false}},"next":{"fields":{"slug":"/react/html-node-streaming/"}},"previous":{"fields":{"slug":"/typescript/typescript-as-const/"}}},{"node":{"id":"f5dbfa6e-986a-5edb-9bc0-2f30132ce702","excerpt":"원글: https://aralroca.com/blog/html-node-streaming 썸네일 최근 몇 년간 브라우저는 HTML과 자바스크립트를 스트리밍을 지원하기 시작했습니다. 이 글에서는 이에 대한 장점과 브라우저가 자동으로 수행하지 않는 다른 작업들을 통해 스트리밍의 이점을 최대한 활용할 수 있는 방법에 대해 이야기할 것입니다. 스트리밍 HTML 초기 로드하는 동안에는 브라우저가 자동으로 처리하기 때문에 크게 신경 쓸 필요가 없습니다. 스트리밍 하는 동안 HTML 청크를 받으면, 브라우저는 그 콘텐츠를 출력합니다. 서버에서 스트리밍을 활성화하려면 헤더를 조정해야 합니다. 아래는 예시입니다. 그리고 응답에서는 ReadableStream을 사용합니다. 이것은 Bun을 사용한 예시입니다. enqueue 안에 있는 각 문자열은 브라우저가 받게 될 청크입니다. 스트리밍 중 HTML 콘텐츠 변경 성능상의 이점이 많기 때문에 많이 사용되는 방법 중 하나는 스트리밍 되는 동안 HTML 콘…","fields":{"slug":"/react/html-node-streaming/"},"frontmatter":{"categories":"translate react","title":"(번역) 스트리밍 HTML과 DOM 비교 알고리즘","date":"March 21, 2024","draft":false}},"next":{"fields":{"slug":"/browser/browser-coordinate-size-api/"}},"previous":{"fields":{"slug":"/typescript/as-const-vs-satisfies/"}}},{"node":{"id":"e5b56e8b-108e-5455-8e4a-ebbfe2c18f09","excerpt":"프론트 개발을 하다보면 웹 페이지의 다양한 요소들을 직접 제어해야 하는 경우가 있습니다. 이 글에서는 CSSOM의 네 가지 주요 좌표 체계인 Offset, Page, Viewport, Screen에 대해 자세히 알아보고, 위치와 크기 관련 API는 어떤것이 있는지 살펴볼 예정입니다. 이 글을 통해 웹 페이지 내에서 요소의 위치와 크기를 정밀하게 제어하는 데 도움이 되길 바랍니다. 표준 CSSOM 좌표 체계 보통 클라이언트 개발에서 좌표의 원점은 왼쪽 하단이 아닌, 왼쪽 상단 입니다. CSS 객체 모델에서는 Offset, Page, Viewport, Screen 네 가지 표준 좌표 체계가 있습니다. 출처: MDN Coordinate_systems Offset 정의: 오프셋 좌표 체계는 특정 요소의 상대적 위치를 나타내는 데 사용됩니다. 이는 요소의 왼쪽 상단 모서리를 시작점(원점)으로 합니다. 특징: 이 좌표 체계는 요소의 부모 요소를 기준으로 한 상대적 위치를 제공합니다. 마…","fields":{"slug":"/browser/browser-coordinate-size-api/"},"frontmatter":{"categories":"browser","title":"브라우저 위치 및 크기 관련 API들","date":"March 03, 2024","draft":false}},"next":{"fields":{"slug":"/infra/docker-command/"}},"previous":{"fields":{"slug":"/react/html-node-streaming/"}}},{"node":{"id":"4cbf47cf-4a24-5ffd-88c3-bdede11e8a72","excerpt":"Docker 엔진 시작하기/종료하기 도커 엔진: 자동 실행 설정 활성화시, 컴퓨터를 켰을 때 함께 자동으로 실행 Docker Desktop은 도커 엔진 자동으로 실행하도록 설정 되어있음. 리눅스 명령어로 엔진 시작, 종료, 자동 실행 설정 방법 systemctrl: 서비스 시작 및 중지 관련 리눅스 명령어 (system control) 컨테이너: 실행시킬 스크립트가 따로 필요. 컴퓨터를 켰을 때 함께 자동으로 실행 되지 않음. 컨테이너의 기본적인 사용 방법 컨테이너를 다루는 모든 명령어는 docker 명령어로 시작 도커 명령어 구성: docker 커맨드(무엇을 어떻게) 대상 + 옵션, 인자: docker 커맨드(무엇을 어떻게) 옵션 대상 인자 상위 커맨드는 생략 가능하다?! ex) docker run, docker start 커맨드(무엇을 어떻게)는 상위커맨드와 하위커맨드로 나뉘어짐. container 상위 커맨들를 생략하는 경우가 있음. 도커 1.13부터 커맨드가 재편되면서 …","fields":{"slug":"/infra/docker-command/"},"frontmatter":{"categories":"infra","title":"docker에서 자주쓰는 명령어","date":"February 18, 2024","draft":false}},"next":{"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}},"previous":{"fields":{"slug":"/browser/browser-coordinate-size-api/"}}},{"node":{"id":"49246cb3-c4b8-5aeb-a4c5-a1b737e89b51","excerpt":"원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939 CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다. 이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다. 읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다. 약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리…","fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"},"frontmatter":{"categories":"translate a11y","title":"(번역) 접근성을 고려하여 CSS 작성하기","date":"February 18, 2024","draft":false}},"next":{"fields":{"slug":"/react/storybook-react-server-components/"}},"previous":{"fields":{"slug":"/infra/docker-command/"}}},{"node":{"id":"5fc01a99-7197-518c-a9de-60ace2976502","excerpt":"원문: https://storybook.js.org/blog/storybook-react-server-components/ 스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용 리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다. 가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다. 이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 …","fields":{"slug":"/react/storybook-react-server-components/"},"frontmatter":{"categories":"translate react","title":"(번역) 리액트 서버 컴포넌트를 위한 스토리북","date":"February 02, 2024","draft":false}},"next":{"fields":{"slug":"/react/next-app-router-react-query/"}},"previous":{"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}}},{"node":{"id":"161feee4-18f2-5161-8900-a9bc5eda4367","excerpt":"지난 글에서 react-query의 hydrate, dehydrate을 통해 서버에서 prefetching 한 데이터 사용하는 방법에 대해서 살펴보았습니다. 서버에서 prefetching 한 데이터 사용하기 오늘은 조금 실용적으로 Next.js 13, 14 버전의 app router에서 react-query를 어떻게 사용하고 세팅하면 좋을지 고민했던 내용에 대해서 이야기해보도록 하겠습니다. 해당 글은 23년 10월에 메이저 버전 업데이트된, Next.js 14와 React-Query 5 를 기준으로 작성된 글입니다. 예시로 보여주는 코드는 저의 next-14-react-query repo에서 확인 할 수 있습니다. 제가 크게 고민했던 문제는 아래 세 가지입니다. Hydrate vs ReactQueryStreamedHydration 두 가지 API 중에 어떤 것을 채택할까? Hydration API 사용 시에 RSC, RCC 모두에서 깔끔한 코드를 유지하려면 queryOption은 …","fields":{"slug":"/react/next-app-router-react-query/"},"frontmatter":{"categories":"react react-query","title":"Next.js app router에서 React Query 사용하면서 고민했던 것들","date":"January 07, 2024","draft":false}},"next":{"fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"}},"previous":{"fields":{"slug":"/react/storybook-react-server-components/"}}},{"node":{"id":"55331269-8b15-516d-aa6d-7eb90d2fedee","excerpt":"원문: https://javascript.plainenglish.io/the-5-most-innovative-proposals-for-javascript-planned-for-2024-22139dd2f546 JavaScript Logo 2024 자바스크립트가 빠른 속도로 발전함에 따라 2024년은 이 프로그래밍 언어에 있어 중요한 해가 될 것입니다. 이 글에서는 개발자들이 자바스크립트 코딩에 접근하는 방식을 재정의하기 위한 최신 제안을 자세히 살펴보겠습니다. 이 글이 유용하길 바랍니다! 시작해 봅시다! 목차 데코레이터 Temporal API 파이프라인 연산자 Error cause 레코드와 튜플 1. 데코레이터 자바스크립트에 데코레이터가 도입된 것은 개발자가 클래스, 메서드, 프로퍼티 및 매개변수의 동작을 조작하고 개선할 수 있는 방법이 크게 발전했음을 의미합니다. 파이썬과 타입스크립트와 같은 다른 프로그래밍 언어에서 유래한 데코레이터는 기능을 추가하거나 수정하는 간결하고 선언적인…","fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"},"frontmatter":{"categories":"translate javascript","title":"(번역) 2024년 자바스크립트의 5가지 가장 혁신적인 제안","date":"December 20, 2023","draft":false}},"next":{"fields":{"slug":"/react/server-rendering-and-react-query/"}},"previous":{"fields":{"slug":"/react/next-app-router-react-query/"}}},{"node":{"id":"d77b67b4-3ea0-53b0-b513-8a24dd4da08d","excerpt":"Next.js나 Remix 같은 프레임워크 내에서 React-Query를 사용한다면, 서버 렌더링 될 때 요청 후 응답받은 데이터를 SPA 방식으로 전환되고 나서도 유지할 수 있을까요? 어떻게 가능할까요? React Query의 hydrate와 dehydrate는 서버에서 미리 가져온 데이터를 클라이언트 사이드에서 재사용 할 수 있게 해줍니다. 이번 글을 통해 서버 렌더링과 어떻게 이를 가능하게 하는지 hydrate와 dehydrate에 대해서 알아봅시다. Server Rendering 서버 렌더링은 사용자가 페이지를 로드하는 즉시 볼 수 있는 초기 HTML을 서버에서 생성하는 행위입니다. 이는 페이지 요청 시 즉시 발생할 수 있으며(SSR), 이전 요청이 캐시 되었거나 빌드 시간에 미리 생성(SSG) 할 수도 있습니다. 클라이언트 렌더링 애플리케이션에서는 사용자에게 화면에 콘텐츠를 표시하기 전에 최소 3번의 서버 왕복(roundtrips)이 필요합니다. 서버 렌더링은 위의 과정…","fields":{"slug":"/react/server-rendering-and-react-query/"},"frontmatter":{"categories":"react react-query","title":"서버에서 React Query prefetching 한 데이터 사용하기","date":"December 10, 2023","draft":false}},"next":{"fields":{"slug":"/react/introducing-signals/"}},"previous":{"fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"}}},{"node":{"id":"9c6b2391-e371-545b-b36b-85a35b38a7ac","excerpt":"원문: https://preactjs.com/blog/introducing-signals/ 시그널은 앱이 복잡해져도 빠른 속도를 유지하도록 하는 상태 표현 방식입니다. 시그널은 반응형 원칙에 기반을 두고 있으며, 가상 돔에 최적화된 독특한 구현을 통해 개발자에게 훌륭한 경험을 제공합니다. 본질적으로 시그널은 특정 값을 가지고 있는 .value 속성을 가진 객체입니다. 컴포넌트 내에서 시그널의 value 속성에 접근하면, 그 시그널의 값이 변경될 때 해당 컴포넌트가 자동으로 업데이트됩니다. 이는 간단하고 작성하기 쉬울 뿐만 아니라, 앱이 얼마나 많은 컴포넌트를 가지고 있든 상태 업데이트가 빠르게 유지되도록 보장합니다. 시그널은 기본적으로 빠르며, 백그라운드에서 자동으로 업데이트를 최적화해줍니다. REPL에서 실행 시그널은 훅과 달리 컴포넌트 내부 또는 외부에서 사용할 수 있습니다. 또한 시그널은 훅과 클래스 컴포넌트 모두에서 훌륭하게 작동하므로, 기존 지식을 활용하며 자신의 속도에 …","fields":{"slug":"/react/introducing-signals/"},"frontmatter":{"categories":"translate react","title":"(번역) 시그널(Signal)에 대한 소개","date":"November 27, 2023","draft":false}},"next":{"fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"}},"previous":{"fields":{"slug":"/react/server-rendering-and-react-query/"}}},{"node":{"id":"5f49b3c9-9991-5b3e-9e49-46ec9a85c02d","excerpt":"원글: https://itnext.io/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components-82e46b5820c 프런트엔드 개발 영역에서는 용어와 패러다임이 때로는 이해하기 어려울 수 있으며 ‘헤드리스 UI’ 또는 ‘헤드리스 컴포넌트’도 이 범주에 속할 수 있습니다. 이러한 용어들이 무엇을 의미하는지 궁금해서 고개를 갸웃거리고, 혼자만 그런 것이 아닙니다. 사실, 혼란스러운 이름에도 불구하고 이러한 개념들은 복잡한 사용자 인터페이스 관리를 상당히 단순화할 수 있는 매력적인 전략입니다. 헤드리스 컴포넌트는 난해해 보일 수 있지만, 그 진정한 힘은 유연성, 재사용 가능성, 그리고 코드베이스의 구성과 깔끔함을 향상시킬 수 있는 능력에 있습니다. 이 글에서는 이 패턴이 정확히 무엇인지, 왜 유용한지, 그리고 인터페이스 디자인에 대한 접근 방식을 어떻게 혁신할 수 있는지에 대해 탐구해 볼 것입니다. …","fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"},"frontmatter":{"categories":"translate react","title":"(번역) React에서 UI와 로직 분리하기:헤드리스 컴포넌트를 사용한 클린 코드 접근법","date":"September 13, 2023","draft":false}},"next":{"fields":{"slug":"/cs/design-patterns-for-humans/"}},"previous":{"fields":{"slug":"/react/introducing-signals/"}}},{"node":{"id":"44fd3704-304a-5b2a-8702-af196e247d28","excerpt":"원글: https://github.com/kamranahmedse/design-patterns-for-humans/blob/master/readme.md 역자주: 이 글은 Kamran Ahmed의 글이며, PHP-7 기준으로 작성된 예제 코드를 타입스크립트로 변경하였습니다. logo 🎉 디자인 패턴에 대한 초간단 설명입니다! 🎉 누구든지 혼란스럽게 만들 수 있는 주제입니다. 이 글에서는 최대한 간단한 방식으로 설명함으로써 당신의 기억 속 (그리고 아마도 제 기억)에 각인시키기 위해 노력할 것입니다. 저의 다른 프로젝트도 확인하고 트위터에서 “안녕”이라고 인사해 보세요. 생성 패턴 구조 패턴 행동 패턴 심플 팩토리(Simple Factory) 어댑터(Adapter) 책임 연쇄(Chain of Responsibility) 팩토리 메서드(Factory Method) 브릿지(Bridge) 명령(Command) 추상 팩토리(Abstract Factory) 컴포지트(Composite) 반복자…","fields":{"slug":"/cs/design-patterns-for-humans/"},"frontmatter":{"categories":"translate cs","title":"(번역) 우리들을 위한 디자인 패턴","date":"August 27, 2023","draft":false}},"next":{"fields":{"slug":"/javascript/garbage-collection/"}},"previous":{"fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"}}},{"node":{"id":"7e56bfba-a382-5266-96c6-f581980fe75d","excerpt":"힙과 스택 메모리의 차이를 살펴보고, 자바스크립트 대표 엔진인 V8의 Resident set 구조와 Minor GC, Major GC 의 동작 알고리즘에 대해서 자세하게 살펴 보도록 하겠습니다. 이 글이 가비지 컬렉터가 어떻게 동작하는지 이해하는데 도움이 되길 바랍니다. V8 메모리 구조 (feat. Resident set) Resident Set은 실행 중인 프로그램의 메모리 관리를 위해 사용되는 용어로, 현재 V8 프로세스가 사용하는 모든 메모리를 나타냅니다. 이것은 JavaScript 코드, 객체, 함수, 변수 등을 저장하는 데 필요한 모든 메모리를 포함합니다. Resident Set은 다음과 같이 여러 세그먼트로 구성됩니다. \n출처: memory management in V8 stack vs heap 메모리 공간은 크게 스택 메모리(Stack memory)와 힙 메모리(Heap memory)로 구분되며, 힙 메모리는 다양한 목적으로 서비스하는 여러 공간으로 더 나누어집니다.…","fields":{"slug":"/javascript/garbage-collection/"},"frontmatter":{"categories":"javascript","title":"자바스크립트 가비지 컬렉션 알고리즘","date":"July 14, 2023","draft":false}},"next":{"fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"}},"previous":{"fields":{"slug":"/cs/design-patterns-for-humans/"}}},{"node":{"id":"1a5d4b16-06aa-572e-802e-98a3de358758","excerpt":"원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …","fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"},"frontmatter":{"categories":"translate react react-query","title":"(번역) React Query를 사용하여 서버 상태를 관리하는 방법","date":"June 21, 2023","draft":false}},"next":{"fields":{"slug":"/cs/6-caching-strategies/"}},"previous":{"fields":{"slug":"/javascript/garbage-collection/"}}},{"node":{"id":"872218ff-8507-5910-8001-bc52d72fca27","excerpt":"원글: https://javascript.plainenglish.io/6-caching-strategies-to-remember-while-designing-cache-system-da058a3757cf 캐시 시스템 관련 용어, 읽기 vs 쓰기 중심의 애플리케이션에서 캐싱 전략, 캐시를 무효화하는 방법 등에 대해서 알려드리겠습니다. 개요 캐싱의 목표는 원본 소스에서 데이터를 가져오는 횟수를 줄여 처리 속도를 높이고 대기 시간을 감소시키는 것입니다. 캐싱은 인메모리 캐싱, 디스크 캐싱, 데이터베이스 캐싱, CDN 캐싱과 같은 다양한 수준의 아키텍처에서 구현될 수 있습니다. 데이터는 각각 고유한 이점이 있는 다양한 기술을 사용하여 캐싱할 수 있습니다. 인메모리 캐싱은 컴퓨터의 주 메모리에 데이터를 저장하여 디스크 저장소 보다 빠른 액세스를 제공합니다. 반면 디스크 캐싱은 하드 디스크에 데이터를 저장하므로 주 메모리보다는 느리지만 원격 소스에서 데이터를 가져오는 것보다 빠릅니다. 데이터…","fields":{"slug":"/cs/6-caching-strategies/"},"frontmatter":{"categories":"translate cs","title":"(번역) 캐시 시스템 설계할 때 기억해야 할 6가지 캐싱 전략","date":"June 13, 2023","draft":false}},"next":{"fields":{"slug":"/javascript/metaprogramming/"}},"previous":{"fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"}}},{"node":{"id":"7268c9ab-b8e9-5638-aa7a-c6c2e2d4f1a8","excerpt":"ES6에서는 Proxy, Reflect 객체를 통해 메타프로그래밍을 할 수 있습니다. 메타프로그래밍이 무엇인지? 그리고 Proxy와 Reflect에 대해서 알아보도록 하겠습니다. Metaprogramming 메타프로그래밍은 프로그램이 자기 자신을 조작할 수 있는 능력을 말합니다. 이를 통해 프로그램은 동적으로 자기 자신을 변화시키거나, 다른 프로그램을 분석하거나, 새로운 코드를 생성할 수 있습니다. 메타프로그래밍을 통해 코드의 유연성과 재사용성을 높일 수 있으며, 런타임에서 객체의 동작을 수정할 수 있어서 더욱 강력한 코드를 작성할 수 있습니다. 메타프로그래밍에서 가장 중요한 세 가지 개념 Introspection Introspection은 코드가 자기 자신을 검사할 수 있는 능력을 의미합니다. 이를 통해 코드의 내부 구조와 데이터를 검사하거나, 코드의 실행 시점에서 객체의 메소드나 프로퍼티에 접근할 수 있습니다. 자바스크립트에서는 ES6부터 Reflect 객체를 제공하여 Intr…","fields":{"slug":"/javascript/metaprogramming/"},"frontmatter":{"categories":"javascript","title":"Proxy, Reflect와 메타프로그래밍","date":"May 07, 2023","draft":false}},"next":{"fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"}},"previous":{"fields":{"slug":"/cs/6-caching-strategies/"}}},{"node":{"id":"09bde05f-002e-5468-8934-6141fab54678","excerpt":"원문: https://blog.openreplay.com/deep-copying-objects-with-the-structuredclone-api/ Deep Copying Objects with the StructuredClone API 개요: 자바스크립트에서 객체를 복사하는 것은 간단하지 않으며, 이는 잘 알려진 문제입니다. 그러나 이 글에서는 해결책을 제공합니다. StructuredClone API를 사용하면 모든 객체를 간단하고 빠르게 복사할 수 있습니다. 자바스크립트에서 객체가 변수에 저장될 때, 해당 변수는 객체의 참조값을 갖습니다. 이는 변수 자체에 객체를 저장하는 것이 아니라, 객체의 메모리 위치를 나타내는 식별자를 저장한다는 것을 의미합니다. 객체의 복사는 원시 타입과는 다른 방식으로 동작합니다. 얕은 복사(Shallow Copy) vs. 깊은 복사(Deep Copy) 자바스크립트에서 값은 두 가지 방법으로 복사할 수 있습니다. 얕은 복사와 깊은 복사입니다. 얕은 복…","fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"},"frontmatter":{"categories":"translate javascript","title":"(번역) StructuredClone API를 사용하여 객체를 깊은 복사하는 법","date":"May 07, 2023","draft":false}},"next":{"fields":{"slug":"/feature/input-date/"}},"previous":{"fields":{"slug":"/javascript/metaprogramming/"}}},{"node":{"id":"7dd77d91-4c83-5f76-92b0-9e9b60089ea7","excerpt":"Date와 인연이 깊은지 날짜 선택 UI를 custom 해서 개발해야 하는 경험을 두 번째 하게 되었다. mui datepicker를 붙이기엔 너무 과해서 react-calendar 라이브러리를 사용하려고 했는데, 문제는 input은 함께 딸려오지 않기 때문에 input 관련 interaction은 직접 구현해야 했다. 인 하우스용 서비스라 여러 라이브러리 참고해서 직접 인터렉션을 정의하고 구현해 보았는데 기록 차 경험을 남겨본다! TL;DR; 전체 구현 결과물은 code sandbox에서 확인할 수 있다. DatePicker 컴포넌트를 개발해 보자! (calendar는 라이브러리를 사용) 딱히 input에 대한 큰 요구사항이 없이 기능 개발을 해야 하는 경우 그냥 react-date-picker를 써라 DatePicker 컴포넌트 개발 dependency react-calendar dayjs props 설계 minDate, maxDate: date range를 지원해야하므로 …","fields":{"slug":"/feature/input-date/"},"frontmatter":{"categories":"feature","title":"날짜 입력 input 인터렉션 개발기","date":"April 23, 2023","draft":false}},"next":{"fields":{"slug":"/react/recoil-introduction/"}},"previous":{"fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"}}},{"node":{"id":"08e40669-d8eb-531c-ab48-fcad02ba39ef","excerpt":"React 18로 버전업 하면서 프로젝트에 Recoil을 도입했는데, 그 과정에서 고민했던 내용을 기록차 남겨본다.\n TL;DR; 개념 정리나 장단점 비교에 대해서는 이미 알고 계신분들도 있을 것 같아서 폴더구조나 Redux를 대체하는게 맞는가에 대해서만 먼저 간단하게 요약한 내용을 공유한다. 1. 폴더 구조는 data source 별로 나누자 Suggested folder structure for atoms and selectors recoil 폴더 하위에 data source 별로 나누자 data source 하위에 atoms, selectors, hooks를 두자 2. concurrency mode 사용하려면 Redux는 버리고 Recoil로 넘어가야하나? useSyncExternalStore hook 을 사용해서 Redux에서도 동시성 모드를 사용할 수 있다. 이 부분에 대해서 고민해 본 내용은 아래에.. Recoil 도입 배경 Recoil은 Facebook에서 개발한 상태…","fields":{"slug":"/react/recoil-introduction/"},"frontmatter":{"categories":"react","title":"Recoil 도입기(feat. 폴더구조)","date":"April 08, 2023","draft":false}},"next":{"fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"}},"previous":{"fields":{"slug":"/feature/input-date/"}}},{"node":{"id":"e19abd3b-8537-528d-a31e-3a74ebbb5cc5","excerpt":"Introduction #1077 이슈를 보면 mutateFn의 variable을 필수값으로 변경한 이력을 볼 수 있다. mutation인데 왜 variable이 없냐고? 나의 경우에는 URL의 있는 queryParams를 읽어와서 데이터를 넘겨주면 되기 때문에 useSomething hook 안에서 처리하고 싶었다. 문제상황 mutate() 와 같이 variable에 아무 값도 넣지 않고 mutation 함수를 호출 할 수 없다.\nTVariables 타입에 undefined도 줘보고 이래저래 별짓을 다해봐도 필수값을 optional 하게 바꿀수는 없었다. 에러 발생\n 해결 실질적으로는 mute 함수에서 variable을 정의해서 넣어주는 것이기 때문에 그렇게 꼼수해결법도 아니다.","fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"},"frontmatter":{"categories":"react react-query troubleshooting","title":"React Query useMutation에서 variable 옵셔널하게 사용하기","date":"February 15, 2023","draft":false}},"next":{"fields":{"slug":"/react/react-query-basic/"}},"previous":{"fields":{"slug":"/react/recoil-introduction/"}}},{"node":{"id":"e1211b4f-4197-5605-879f-45d011d0d210","excerpt":"Introduction 이 글을 읽기전 React의 상태관리 종류 4가지를 먼저 읽으면 이해가 쉬울 수 있다. 얼마전 회사 프로젝트에 react-query를 도입하여 사용중인데 정말 너무너무 좋다. react-query를 이용해서 server state를 분리하다보니 loading 처리나, refetch 등 불필요한 코드가 많이 줄었다. 뭐든지 처음이 어렵지 큰 그림을 알고있으면 도입하여 적용하는데에는 큰 문제가 없고, 사용하면서 상황에 맞게 옵션과 아키텍쳐를 변경하면 된다고 생각하기 때문에, 이번 포스팅에서는 react-query를 적용하기 위해 필요한 전반적인 개념을 정리해보고자 한다. 용도 react-query는 서버 데이터 관리를 위한 라이브러리다. 이 라이브러리는 API 호출, 캐시 및 상태 관리, 오류 처리 등의 작업을 간단하게 처리할 수 있도록 도와준다. 서버 데이터 관리는 중요한 부분 중 하나다. 클라이언트에서는 서버로부터 데이터를 가져와(Read) 화면에 그려주고…","fields":{"slug":"/react/react-query-basic/"},"frontmatter":{"categories":"react react-query","title":"React Query 시작하기 (feat. Tanstack)","date":"February 05, 2023","draft":false}},"next":{"fields":{"slug":"/react/react-state-management/"}},"previous":{"fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"}}},{"node":{"id":"afca1d77-1bf9-59de-8227-74dfb0b19eed","excerpt":"Introduction react의 상태관리 종류 4가지를 알아보고, 각각의 종류별로 왜 다르게 관리해야하는지와 React에서 제공하는 API 혹은 어떤 라이브러리를 사용해서 관리하면 좋을지 알아보도록 하자. 상태관리 종류 Local state Global state App 어느곳(여러 컴포넌트)에서 state를 확인하거나 업데이트하기 위해서 필요함. 예: authenticated user state - logged in/out Server state 서버로 받은 데이터가 UI 상태와 통합되어야 하는 경우 필요함. 예: loading, error… 대표 라이브러리: SWR, react-query URL state URL에 존재하는 데이터(pathname, query params..) 1) Local state useState useReducer useReducer vs useState 차이 useState는 하나의 state operation만 가능했다면, useReducer는 여…","fields":{"slug":"/react/react-state-management/"},"frontmatter":{"categories":"react","title":"React의 상태관리 종류 4가지","date":"February 03, 2023","draft":false}},"next":{"fields":{"slug":"/react/error-handle/"}},"previous":{"fields":{"slug":"/react/react-query-basic/"}}},{"node":{"id":"0e971c95-1f7f-53c0-b77f-9ed5af3af6e0","excerpt":"고민 혹시 제가 내린 결론에 다른 의견을 주신다면 매우 감사합니다. 🙇🏻‍♀️ 에러를 발생 vs 사전 확인을 통해 예외 처리 어느 부분까지 사전 확인이 필요한 걸까? 고민하게 된 배경 함수의 매개변수가 의도한 것과 다른 값이 들어왔을 때 에러가 발생할 수 있다. 그런데 문득 에러를 발생하게 하는 게 올바른 것인가? 아니면 애초에 에러가 발생할 상황을 하나도 빠짐없이 막는 것이 올바른 것인가? 고민이 들었다. 위의 코드를 보면 event 객체가 무조건 존재할 상황을 가졍하고 있으므로 만약 비정상적인 event 객체가 첫 번째 인자로 전달된다면 에러가 발생할 것이다. 그렇다면 이렇게 코드를 짜는 것이 좋을까? 애초에 event 객체가 존재하지 않을 수 있는 케이스를 고려해서 절대로 에러를 발생하지 않게 하는 것이 좋을까? 결론 App을 Crashed 시키지 않는 한해서 에러가 발생해야 하는 경우는 발생하게 내버려 두고, 예상할 수 있는 예외는 try…catch를 이용하여 에러가 전파되지 않도…","fields":{"slug":"/react/error-handle/"},"frontmatter":{"categories":"react","title":"에러 핸들링에 대한 고민 (feat. React)","date":"October 16, 2022","draft":false}},"next":{"fields":{"slug":"/browser/touch-mouse-event/"}},"previous":{"fields":{"slug":"/react/react-state-management/"}}},{"node":{"id":"47a3c1ed-8f39-56be-81c1-c0cd470b4c41","excerpt":"문제상황 dropdown 컴포넌트를 만들었는데, focus가 다른곳으로 이동하면 펼쳐져있던 dropdown이 접혀져야 해서 blur 이벤트 발생시 접히도록 했다. 그랬더니 펼쳐져 있는 항목 중 하나를 click 했을때, 위에서 설정한 blur 이벤트가 먼저 발생하면서 click 이벤트가 호출되지 않는 이슈가 문제였다. dropdown_비정상동작.gif 문제의 코드 TL;DR Touch, mouse 이벤트 순서 touchstart touchmove touchend mouseover mousemove mousedown mouseup click 해결방법 click시 처리해줘야 하는 이벤트 핸들러를 click 이 아닌 mousedown 에서 처리해준다. dropdown_정상동작.gif Dropdown CodeSandbox Touch and mouse 이벤트 그러면 지금부터는 web.dev 문서를 살펴보면서 알게된 touch 이벤트의 도입 배경과 touch, mouse 이벤트를 함께 다루기…","fields":{"slug":"/browser/touch-mouse-event/"},"frontmatter":{"categories":"feature browser","title":"Touch, Mouse 이벤트 함께 다루기 (feat. dropdown)","date":"September 18, 2022","draft":false}},"next":{"fields":{"slug":"/infra/jenkins-bitbucket/"}},"previous":{"fields":{"slug":"/react/error-handle/"}}},{"node":{"id":"bacf9614-be80-5449-8b45-fa90310ef8dd","excerpt":"목표 Jenkins pipeline을 이용하여, bitbucket에서 PR 생성시 Jenkins job을 실행시키고 그 결과를 slack으로 전송한다. (Jenkins 내의 credential 설정이나 자세한 Jenkins 플러그인 설정 사항은 해당 글에서 포함하지 않음.) Webhook 웹훅이란 뭘까? 위의 목표를 이루려면 우리는 웹훅이 필요하다. 어플리케이션에 한정하여 특정 이벤트가 발생할 때 지정된 URL로 push 알림을 보내는 것이 webhook이다. callback 이랑 비슷한데, 조금 더 작은 범위라고 생각하면 된다. 이 방법은 주기적으로 검사하여 변경 사항이 있는지 확인하는 것보다 훨씬 효율적이다. 구현 방법 우리는 bitbucket에서 PR이 생성되었을때 Jenkins 플러그인인 Generic Webhook Trigger를 호출 하고, Jenkins Job 실행이 끝났을 때 slack Webhook을 호출 할 것이다. 1. Jekins에서 Generic Webho…","fields":{"slug":"/infra/jenkins-bitbucket/"},"frontmatter":{"categories":"infra","title":"Bitbucket과 Jenkins를 연동하여 테스트 자동화 (feat. Generic Webhook Trigger)","date":"September 04, 2022","draft":false}},"next":{"fields":{"slug":"/feature/confirm/"}},"previous":{"fields":{"slug":"/browser/touch-mouse-event/"}}},{"node":{"id":"2b300262-3404-5526-8a92-b3c887f4e178","excerpt":"보통 어플리케이션에서 공통된 디자인의 confirm 창을 사용한다. 이때, window.confirm과 같이 고객의 OK/CANCEL 클릭 여부에 따라서 다음 동작을 이어나가게 하고싶다면 어떻게 구현해야할까? 준비물 Modal Component (global 하게 등록) Global State Modal Hook 보통 웹 어플리케이션에서는 공통된 디자인의 confirm을 사용하므로 화면에 보여지는 역할을 하는 Modal Component가 필요하다. 이 컴포넌트는 어플리케이션 전반에 걸쳐서 자주 사용하는 라이브러리이므로 App.js에서 global 하게 로드한다. global 하게 등록된 Modal 컴포넌트를 제어하기 위해서는 어플리케이션 어느 곳에서든 제어 가능하게 하기 위해서 global state가 필요하다. 그래서 modal 관련 state가 변경되었을때, Modal 컴포넌트가 render 되게 한다. 이제 window.confirm 처럼 사용하기 위해 confirm 함수를…","fields":{"slug":"/feature/confirm/"},"frontmatter":{"categories":"feature","title":"Promise를 사용하여 window.confirm 구현하기","date":"August 19, 2022","draft":false}},"next":{"fields":{"slug":"/test/test-introduction/"}},"previous":{"fields":{"slug":"/infra/jenkins-bitbucket/"}}},{"node":{"id":"34e53d64-0467-56f3-8179-68e3f4d6c48c","excerpt":"🔨 다듬을 필요가 있는 글입니다. 테스트 flow 테스트 코드 작성 → 테스트 읽고 → 실행 → 출력(Reporter) 테스트 종류 단위 테스트: 모듈을 분리된 환경에서 테스트 의존성이 있는 모듈을 제어하기 위해 필연적으로 모의 객체(Mocking)을 사용할 수밖에 없으며, 이 경우 각 모듈이 실제로 잘 연결되어 상호 작용하는지에 대해서는 검증하지 못한다. 통합 테스트: 2개 이상의 모듈 간의 상호작용을 테스트 (개발자 관점의 테스트) 단위 테스트에 비해 mocking을 덜하며, 모듈 간의 연결에서 발생하는 에러를 검증할 수 있다. E2E 테스트: 사용자의 실행 환경과 거의 동일한 환경에서 테스트를 진행 (사용자 관점의 테스트) 세부 모듈들이 갖는 다양한 상황들의 조합을 고려해야 하기 때문에 테스트 자바스크립트 테스트 도구 자바스크립트 테스트는 도구들은 아래와 같이 분류할 수 있다. Test Runners Testing Frameworks Assertion Libraries Tes…","fields":{"slug":"/test/test-introduction/"},"frontmatter":{"categories":"test","title":"자바스크립트 테스트 개념정리","date":"April 14, 2022","draft":false}},"next":null,"previous":{"fields":{"slug":"/feature/confirm/"}}}],"categories":["All","framework","react","next","translate","typescript","browser","infra","a11y","react-query","javascript","cs","feature","troubleshooting","test"]}}, "staticQueryHashes": ["1073350324","1956554647","2938748437"]} \ No newline at end of file diff --git a/page-data/posts/react-query/page-data.json b/page-data/posts/react-query/page-data.json index 2b0a31e9..21373853 100644 --- a/page-data/posts/react-query/page-data.json +++ b/page-data/posts/react-query/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-category-template-js", "path": "/posts/react-query", - "result": {"pageContext":{"currentCategory":"react-query","categories":["All","framework","react","next","translate","typescript","browser","a11y","infra","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"161feee4-18f2-5161-8900-a9bc5eda4367","excerpt":"지난 글에서 react-query의 hydrate, dehydrate을 통해 서버에서 prefetching 한 데이터 사용하는 방법에 대해서 살펴보았습니다. 서버에서 prefetching 한 데이터 사용하기 오늘은 조금 실용적으로 Next.js 13, 14 버전의 app router에서 react-query를 어떻게 사용하고 세팅하면 좋을지 고민했던 내용에 대해서 이야기해보도록 하겠습니다. 해당 글은 23년 10월에 메이저 버전 업데이트된, Next.js 14와 React-Query 5 를 기준으로 작성된 글입니다. 예시로 보여주는 코드는 저의 next-14-react-query repo에서 확인 할 수 있습니다. 제가 크게 고민했던 문제는 아래 세 가지입니다. Hydrate vs ReactQueryStreamedHydration 두 가지 API 중에 어떤 것을 채택할까? Hydration API 사용 시에 RSC, RCC 모두에서 깔끔한 코드를 유지하려면 queryOption은 …","fields":{"slug":"/react/next-app-router-react-query/"},"frontmatter":{"categories":"react react-query","title":"Next.js app router에서 React Query 사용하면서 고민했던 것들","date":"January 07, 2024","draft":false}},"next":{"fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"}},"previous":{"fields":{"slug":"/react/storybook-react-server-components/"}}},{"node":{"id":"d77b67b4-3ea0-53b0-b513-8a24dd4da08d","excerpt":"Next.js나 Remix 같은 프레임워크 내에서 React-Query를 사용한다면, 서버 렌더링 될 때 요청 후 응답받은 데이터를 SPA 방식으로 전환되고 나서도 유지할 수 있을까요? 어떻게 가능할까요? React Query의 hydrate와 dehydrate는 서버에서 미리 가져온 데이터를 클라이언트 사이드에서 재사용 할 수 있게 해줍니다. 이번 글을 통해 서버 렌더링과 어떻게 이를 가능하게 하는지 hydrate와 dehydrate에 대해서 알아봅시다. Server Rendering 서버 렌더링은 사용자가 페이지를 로드하는 즉시 볼 수 있는 초기 HTML을 서버에서 생성하는 행위입니다. 이는 페이지 요청 시 즉시 발생할 수 있으며(SSR), 이전 요청이 캐시 되었거나 빌드 시간에 미리 생성(SSG) 할 수도 있습니다. 클라이언트 렌더링 애플리케이션에서는 사용자에게 화면에 콘텐츠를 표시하기 전에 최소 3번의 서버 왕복(roundtrips)이 필요합니다. 서버 렌더링은 위의 과정…","fields":{"slug":"/react/server-rendering-and-react-query/"},"frontmatter":{"categories":"react react-query","title":"서버에서 React Query prefetching 한 데이터 사용하기","date":"December 10, 2023","draft":false}},"next":{"fields":{"slug":"/react/introducing-signals/"}},"previous":{"fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"}}},{"node":{"id":"1a5d4b16-06aa-572e-802e-98a3de358758","excerpt":"원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …","fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"},"frontmatter":{"categories":"translate react react-query","title":"(번역) React Query를 사용하여 서버 상태를 관리하는 방법","date":"June 21, 2023","draft":false}},"next":{"fields":{"slug":"/cs/6-caching-strategies/"}},"previous":{"fields":{"slug":"/javascript/garbage-collection/"}}},{"node":{"id":"e19abd3b-8537-528d-a31e-3a74ebbb5cc5","excerpt":"Introduction #1077 이슈를 보면 mutateFn의 variable을 필수값으로 변경한 이력을 볼 수 있다. mutation인데 왜 variable이 없냐고? 나의 경우에는 URL의 있는 queryParams를 읽어와서 데이터를 넘겨주면 되기 때문에 useSomething hook 안에서 처리하고 싶었다. 문제상황 mutate() 와 같이 variable에 아무 값도 넣지 않고 mutation 함수를 호출 할 수 없다.\nTVariables 타입에 undefined도 줘보고 이래저래 별짓을 다해봐도 필수값을 optional 하게 바꿀수는 없었다. 에러 발생\n 해결 실질적으로는 mute 함수에서 variable을 정의해서 넣어주는 것이기 때문에 그렇게 꼼수해결법도 아니다.","fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"},"frontmatter":{"categories":"react react-query troubleshooting","title":"React Query useMutation에서 variable 옵셔널하게 사용하기","date":"February 15, 2023","draft":false}},"next":{"fields":{"slug":"/react/react-query-basic/"}},"previous":{"fields":{"slug":"/react/recoil-introduction/"}}},{"node":{"id":"e1211b4f-4197-5605-879f-45d011d0d210","excerpt":"Introduction 이 글을 읽기전 React의 상태관리 종류 4가지를 먼저 읽으면 이해가 쉬울 수 있다. 얼마전 회사 프로젝트에 react-query를 도입하여 사용중인데 정말 너무너무 좋다. react-query를 이용해서 server state를 분리하다보니 loading 처리나, refetch 등 불필요한 코드가 많이 줄었다. 뭐든지 처음이 어렵지 큰 그림을 알고있으면 도입하여 적용하는데에는 큰 문제가 없고, 사용하면서 상황에 맞게 옵션과 아키텍쳐를 변경하면 된다고 생각하기 때문에, 이번 포스팅에서는 react-query를 적용하기 위해 필요한 전반적인 개념을 정리해보고자 한다. 용도 react-query는 서버 데이터 관리를 위한 라이브러리다. 이 라이브러리는 API 호출, 캐시 및 상태 관리, 오류 처리 등의 작업을 간단하게 처리할 수 있도록 도와준다. 서버 데이터 관리는 중요한 부분 중 하나다. 클라이언트에서는 서버로부터 데이터를 가져와(Read) 화면에 그려주고…","fields":{"slug":"/react/react-query-basic/"},"frontmatter":{"categories":"react react-query","title":"React Query 시작하기 (feat. Tanstack)","date":"February 05, 2023","draft":false}},"next":{"fields":{"slug":"/react/react-state-management/"}},"previous":{"fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"}}}]}}, + "result": {"pageContext":{"currentCategory":"react-query","categories":["All","framework","react","next","translate","typescript","browser","infra","a11y","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"161feee4-18f2-5161-8900-a9bc5eda4367","excerpt":"지난 글에서 react-query의 hydrate, dehydrate을 통해 서버에서 prefetching 한 데이터 사용하는 방법에 대해서 살펴보았습니다. 서버에서 prefetching 한 데이터 사용하기 오늘은 조금 실용적으로 Next.js 13, 14 버전의 app router에서 react-query를 어떻게 사용하고 세팅하면 좋을지 고민했던 내용에 대해서 이야기해보도록 하겠습니다. 해당 글은 23년 10월에 메이저 버전 업데이트된, Next.js 14와 React-Query 5 를 기준으로 작성된 글입니다. 예시로 보여주는 코드는 저의 next-14-react-query repo에서 확인 할 수 있습니다. 제가 크게 고민했던 문제는 아래 세 가지입니다. Hydrate vs ReactQueryStreamedHydration 두 가지 API 중에 어떤 것을 채택할까? Hydration API 사용 시에 RSC, RCC 모두에서 깔끔한 코드를 유지하려면 queryOption은 …","fields":{"slug":"/react/next-app-router-react-query/"},"frontmatter":{"categories":"react react-query","title":"Next.js app router에서 React Query 사용하면서 고민했던 것들","date":"January 07, 2024","draft":false}},"next":{"fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"}},"previous":{"fields":{"slug":"/react/storybook-react-server-components/"}}},{"node":{"id":"d77b67b4-3ea0-53b0-b513-8a24dd4da08d","excerpt":"Next.js나 Remix 같은 프레임워크 내에서 React-Query를 사용한다면, 서버 렌더링 될 때 요청 후 응답받은 데이터를 SPA 방식으로 전환되고 나서도 유지할 수 있을까요? 어떻게 가능할까요? React Query의 hydrate와 dehydrate는 서버에서 미리 가져온 데이터를 클라이언트 사이드에서 재사용 할 수 있게 해줍니다. 이번 글을 통해 서버 렌더링과 어떻게 이를 가능하게 하는지 hydrate와 dehydrate에 대해서 알아봅시다. Server Rendering 서버 렌더링은 사용자가 페이지를 로드하는 즉시 볼 수 있는 초기 HTML을 서버에서 생성하는 행위입니다. 이는 페이지 요청 시 즉시 발생할 수 있으며(SSR), 이전 요청이 캐시 되었거나 빌드 시간에 미리 생성(SSG) 할 수도 있습니다. 클라이언트 렌더링 애플리케이션에서는 사용자에게 화면에 콘텐츠를 표시하기 전에 최소 3번의 서버 왕복(roundtrips)이 필요합니다. 서버 렌더링은 위의 과정…","fields":{"slug":"/react/server-rendering-and-react-query/"},"frontmatter":{"categories":"react react-query","title":"서버에서 React Query prefetching 한 데이터 사용하기","date":"December 10, 2023","draft":false}},"next":{"fields":{"slug":"/react/introducing-signals/"}},"previous":{"fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"}}},{"node":{"id":"1a5d4b16-06aa-572e-802e-98a3de358758","excerpt":"원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …","fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"},"frontmatter":{"categories":"translate react react-query","title":"(번역) React Query를 사용하여 서버 상태를 관리하는 방법","date":"June 21, 2023","draft":false}},"next":{"fields":{"slug":"/cs/6-caching-strategies/"}},"previous":{"fields":{"slug":"/javascript/garbage-collection/"}}},{"node":{"id":"e19abd3b-8537-528d-a31e-3a74ebbb5cc5","excerpt":"Introduction #1077 이슈를 보면 mutateFn의 variable을 필수값으로 변경한 이력을 볼 수 있다. mutation인데 왜 variable이 없냐고? 나의 경우에는 URL의 있는 queryParams를 읽어와서 데이터를 넘겨주면 되기 때문에 useSomething hook 안에서 처리하고 싶었다. 문제상황 mutate() 와 같이 variable에 아무 값도 넣지 않고 mutation 함수를 호출 할 수 없다.\nTVariables 타입에 undefined도 줘보고 이래저래 별짓을 다해봐도 필수값을 optional 하게 바꿀수는 없었다. 에러 발생\n 해결 실질적으로는 mute 함수에서 variable을 정의해서 넣어주는 것이기 때문에 그렇게 꼼수해결법도 아니다.","fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"},"frontmatter":{"categories":"react react-query troubleshooting","title":"React Query useMutation에서 variable 옵셔널하게 사용하기","date":"February 15, 2023","draft":false}},"next":{"fields":{"slug":"/react/react-query-basic/"}},"previous":{"fields":{"slug":"/react/recoil-introduction/"}}},{"node":{"id":"e1211b4f-4197-5605-879f-45d011d0d210","excerpt":"Introduction 이 글을 읽기전 React의 상태관리 종류 4가지를 먼저 읽으면 이해가 쉬울 수 있다. 얼마전 회사 프로젝트에 react-query를 도입하여 사용중인데 정말 너무너무 좋다. react-query를 이용해서 server state를 분리하다보니 loading 처리나, refetch 등 불필요한 코드가 많이 줄었다. 뭐든지 처음이 어렵지 큰 그림을 알고있으면 도입하여 적용하는데에는 큰 문제가 없고, 사용하면서 상황에 맞게 옵션과 아키텍쳐를 변경하면 된다고 생각하기 때문에, 이번 포스팅에서는 react-query를 적용하기 위해 필요한 전반적인 개념을 정리해보고자 한다. 용도 react-query는 서버 데이터 관리를 위한 라이브러리다. 이 라이브러리는 API 호출, 캐시 및 상태 관리, 오류 처리 등의 작업을 간단하게 처리할 수 있도록 도와준다. 서버 데이터 관리는 중요한 부분 중 하나다. 클라이언트에서는 서버로부터 데이터를 가져와(Read) 화면에 그려주고…","fields":{"slug":"/react/react-query-basic/"},"frontmatter":{"categories":"react react-query","title":"React Query 시작하기 (feat. Tanstack)","date":"February 05, 2023","draft":false}},"next":{"fields":{"slug":"/react/react-state-management/"}},"previous":{"fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"}}}]}}, "staticQueryHashes": ["1073350324","1956554647","2938748437"]} \ No newline at end of file diff --git a/page-data/posts/react/page-data.json b/page-data/posts/react/page-data.json index 8fd287a1..e24a8d19 100644 --- a/page-data/posts/react/page-data.json +++ b/page-data/posts/react/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-category-template-js", "path": "/posts/react", - "result": {"pageContext":{"currentCategory":"react","categories":["All","framework","react","next","translate","typescript","browser","a11y","infra","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"909b8521-890b-5dc7-9c04-3289e2502910","excerpt":"process.env 값을 구조 분해 할당 하셨나요? 우리가 환경변수를 접근할 때 객체의 속성값에 접근할 때 사용하는 점 표기법을 사용하기 때문에 구조 분해 할당을 자연스럽게 사용해도 될 것 같은데요. 이런 경우, 왜 에러가 발생하는지 이번 글을 통해 알아보겠습니다. 문제 상황 다음과 같은 코드가 있다고 가정해 봅시다. 위 코드는 process.env 객체에서 API_KEY와 NEXT_PUBLIC_ANALYTICS_ID 변수를 구조 분해 할당하여 사용하려고 합니다. 그러나 Next.js에서는 이 코드가 제대로 작동하지 않을 수 있고, 오류가 발생할 수 있습니다. 왜 이런 문제가 발생할까요? process.env는 일반적인 자바스크립트 객체와는 다르기 때문입니다. 이는 Node.js 환경에서 환경 변수를 담고 있는 특수한 객체로, 모든 속성이 문자열 형태로 저장됩니다. Next.js는 빌드 타임과 런타임에서 환경 변수에 접근하는 방식이 다르므로, 이로 인해 구조 분해 할당을 사용할 …","fields":{"slug":"/react/process-env-destructuring-error.md/"},"frontmatter":{"categories":"react next","title":"환경 변수(process.env)를 구조 분해 할당하면 안되는 이유","date":"June 26, 2024","draft":false}},"next":{"fields":{"slug":"/typescript/typescript-as-const/"}},"previous":{"fields":{"slug":"/astro-hydration/"}}},{"node":{"id":"f5dbfa6e-986a-5edb-9bc0-2f30132ce702","excerpt":"원글: https://aralroca.com/blog/html-node-streaming 썸네일 최근 몇 년간 브라우저는 HTML과 자바스크립트를 스트리밍을 지원하기 시작했습니다. 이 글에서는 이에 대한 장점과 브라우저가 자동으로 수행하지 않는 다른 작업들을 통해 스트리밍의 이점을 최대한 활용할 수 있는 방법에 대해 이야기할 것입니다. 스트리밍 HTML 초기 로드하는 동안에는 브라우저가 자동으로 처리하기 때문에 크게 신경 쓸 필요가 없습니다. 스트리밍 하는 동안 HTML 청크를 받으면, 브라우저는 그 콘텐츠를 출력합니다. 서버에서 스트리밍을 활성화하려면 헤더를 조정해야 합니다. 아래는 예시입니다. 그리고 응답에서는 ReadableStream을 사용합니다. 이것은 Bun을 사용한 예시입니다. enqueue 안에 있는 각 문자열은 브라우저가 받게 될 청크입니다. 스트리밍 중 HTML 콘텐츠 변경 성능상의 이점이 많기 때문에 많이 사용되는 방법 중 하나는 스트리밍 되는 동안 HTML 콘…","fields":{"slug":"/react/html-node-streaming/"},"frontmatter":{"categories":"translate react","title":"(번역) 스트리밍 HTML과 DOM 비교 알고리즘","date":"March 21, 2024","draft":false}},"next":{"fields":{"slug":"/browser/browser-coordinate-size-api/"}},"previous":{"fields":{"slug":"/typescript/as-const-vs-satisfies/"}}},{"node":{"id":"5fc01a99-7197-518c-a9de-60ace2976502","excerpt":"원문: https://storybook.js.org/blog/storybook-react-server-components/ 스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용 리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다. 가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다. 이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 …","fields":{"slug":"/react/storybook-react-server-components/"},"frontmatter":{"categories":"translate react","title":"(번역) 리액트 서버 컴포넌트를 위한 스토리북","date":"February 02, 2024","draft":false}},"next":{"fields":{"slug":"/react/next-app-router-react-query/"}},"previous":{"fields":{"slug":"/infra/docker-command/"}}},{"node":{"id":"161feee4-18f2-5161-8900-a9bc5eda4367","excerpt":"지난 글에서 react-query의 hydrate, dehydrate을 통해 서버에서 prefetching 한 데이터 사용하는 방법에 대해서 살펴보았습니다. 서버에서 prefetching 한 데이터 사용하기 오늘은 조금 실용적으로 Next.js 13, 14 버전의 app router에서 react-query를 어떻게 사용하고 세팅하면 좋을지 고민했던 내용에 대해서 이야기해보도록 하겠습니다. 해당 글은 23년 10월에 메이저 버전 업데이트된, Next.js 14와 React-Query 5 를 기준으로 작성된 글입니다. 예시로 보여주는 코드는 저의 next-14-react-query repo에서 확인 할 수 있습니다. 제가 크게 고민했던 문제는 아래 세 가지입니다. Hydrate vs ReactQueryStreamedHydration 두 가지 API 중에 어떤 것을 채택할까? Hydration API 사용 시에 RSC, RCC 모두에서 깔끔한 코드를 유지하려면 queryOption은 …","fields":{"slug":"/react/next-app-router-react-query/"},"frontmatter":{"categories":"react react-query","title":"Next.js app router에서 React Query 사용하면서 고민했던 것들","date":"January 07, 2024","draft":false}},"next":{"fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"}},"previous":{"fields":{"slug":"/react/storybook-react-server-components/"}}},{"node":{"id":"d77b67b4-3ea0-53b0-b513-8a24dd4da08d","excerpt":"Next.js나 Remix 같은 프레임워크 내에서 React-Query를 사용한다면, 서버 렌더링 될 때 요청 후 응답받은 데이터를 SPA 방식으로 전환되고 나서도 유지할 수 있을까요? 어떻게 가능할까요? React Query의 hydrate와 dehydrate는 서버에서 미리 가져온 데이터를 클라이언트 사이드에서 재사용 할 수 있게 해줍니다. 이번 글을 통해 서버 렌더링과 어떻게 이를 가능하게 하는지 hydrate와 dehydrate에 대해서 알아봅시다. Server Rendering 서버 렌더링은 사용자가 페이지를 로드하는 즉시 볼 수 있는 초기 HTML을 서버에서 생성하는 행위입니다. 이는 페이지 요청 시 즉시 발생할 수 있으며(SSR), 이전 요청이 캐시 되었거나 빌드 시간에 미리 생성(SSG) 할 수도 있습니다. 클라이언트 렌더링 애플리케이션에서는 사용자에게 화면에 콘텐츠를 표시하기 전에 최소 3번의 서버 왕복(roundtrips)이 필요합니다. 서버 렌더링은 위의 과정…","fields":{"slug":"/react/server-rendering-and-react-query/"},"frontmatter":{"categories":"react react-query","title":"서버에서 React Query prefetching 한 데이터 사용하기","date":"December 10, 2023","draft":false}},"next":{"fields":{"slug":"/react/introducing-signals/"}},"previous":{"fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"}}},{"node":{"id":"9c6b2391-e371-545b-b36b-85a35b38a7ac","excerpt":"원문: https://preactjs.com/blog/introducing-signals/ 시그널은 앱이 복잡해져도 빠른 속도를 유지하도록 하는 상태 표현 방식입니다. 시그널은 반응형 원칙에 기반을 두고 있으며, 가상 돔에 최적화된 독특한 구현을 통해 개발자에게 훌륭한 경험을 제공합니다. 본질적으로 시그널은 특정 값을 가지고 있는 .value 속성을 가진 객체입니다. 컴포넌트 내에서 시그널의 value 속성에 접근하면, 그 시그널의 값이 변경될 때 해당 컴포넌트가 자동으로 업데이트됩니다. 이는 간단하고 작성하기 쉬울 뿐만 아니라, 앱이 얼마나 많은 컴포넌트를 가지고 있든 상태 업데이트가 빠르게 유지되도록 보장합니다. 시그널은 기본적으로 빠르며, 백그라운드에서 자동으로 업데이트를 최적화해줍니다. REPL에서 실행 시그널은 훅과 달리 컴포넌트 내부 또는 외부에서 사용할 수 있습니다. 또한 시그널은 훅과 클래스 컴포넌트 모두에서 훌륭하게 작동하므로, 기존 지식을 활용하며 자신의 속도에 …","fields":{"slug":"/react/introducing-signals/"},"frontmatter":{"categories":"translate react","title":"(번역) 시그널(Signal)에 대한 소개","date":"November 27, 2023","draft":false}},"next":{"fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"}},"previous":{"fields":{"slug":"/react/server-rendering-and-react-query/"}}},{"node":{"id":"5f49b3c9-9991-5b3e-9e49-46ec9a85c02d","excerpt":"원글: https://itnext.io/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components-82e46b5820c 프런트엔드 개발 영역에서는 용어와 패러다임이 때로는 이해하기 어려울 수 있으며 ‘헤드리스 UI’ 또는 ‘헤드리스 컴포넌트’도 이 범주에 속할 수 있습니다. 이러한 용어들이 무엇을 의미하는지 궁금해서 고개를 갸웃거리고, 혼자만 그런 것이 아닙니다. 사실, 혼란스러운 이름에도 불구하고 이러한 개념들은 복잡한 사용자 인터페이스 관리를 상당히 단순화할 수 있는 매력적인 전략입니다. 헤드리스 컴포넌트는 난해해 보일 수 있지만, 그 진정한 힘은 유연성, 재사용 가능성, 그리고 코드베이스의 구성과 깔끔함을 향상시킬 수 있는 능력에 있습니다. 이 글에서는 이 패턴이 정확히 무엇인지, 왜 유용한지, 그리고 인터페이스 디자인에 대한 접근 방식을 어떻게 혁신할 수 있는지에 대해 탐구해 볼 것입니다. …","fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"},"frontmatter":{"categories":"translate react","title":"(번역) React에서 UI와 로직 분리하기:헤드리스 컴포넌트를 사용한 클린 코드 접근법","date":"September 13, 2023","draft":false}},"next":{"fields":{"slug":"/cs/design-patterns-for-humans/"}},"previous":{"fields":{"slug":"/react/introducing-signals/"}}},{"node":{"id":"1a5d4b16-06aa-572e-802e-98a3de358758","excerpt":"원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …","fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"},"frontmatter":{"categories":"translate react react-query","title":"(번역) React Query를 사용하여 서버 상태를 관리하는 방법","date":"June 21, 2023","draft":false}},"next":{"fields":{"slug":"/cs/6-caching-strategies/"}},"previous":{"fields":{"slug":"/javascript/garbage-collection/"}}},{"node":{"id":"08e40669-d8eb-531c-ab48-fcad02ba39ef","excerpt":"React 18로 버전업 하면서 프로젝트에 Recoil을 도입했는데, 그 과정에서 고민했던 내용을 기록차 남겨본다.\n TL;DR; 개념 정리나 장단점 비교에 대해서는 이미 알고 계신분들도 있을 것 같아서 폴더구조나 Redux를 대체하는게 맞는가에 대해서만 먼저 간단하게 요약한 내용을 공유한다. 1. 폴더 구조는 data source 별로 나누자 Suggested folder structure for atoms and selectors recoil 폴더 하위에 data source 별로 나누자 data source 하위에 atoms, selectors, hooks를 두자 2. concurrency mode 사용하려면 Redux는 버리고 Recoil로 넘어가야하나? useSyncExternalStore hook 을 사용해서 Redux에서도 동시성 모드를 사용할 수 있다. 이 부분에 대해서 고민해 본 내용은 아래에.. Recoil 도입 배경 Recoil은 Facebook에서 개발한 상태…","fields":{"slug":"/react/recoil-introduction/"},"frontmatter":{"categories":"react","title":"Recoil 도입기(feat. 폴더구조)","date":"April 08, 2023","draft":false}},"next":{"fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"}},"previous":{"fields":{"slug":"/feature/input-date/"}}},{"node":{"id":"e19abd3b-8537-528d-a31e-3a74ebbb5cc5","excerpt":"Introduction #1077 이슈를 보면 mutateFn의 variable을 필수값으로 변경한 이력을 볼 수 있다. mutation인데 왜 variable이 없냐고? 나의 경우에는 URL의 있는 queryParams를 읽어와서 데이터를 넘겨주면 되기 때문에 useSomething hook 안에서 처리하고 싶었다. 문제상황 mutate() 와 같이 variable에 아무 값도 넣지 않고 mutation 함수를 호출 할 수 없다.\nTVariables 타입에 undefined도 줘보고 이래저래 별짓을 다해봐도 필수값을 optional 하게 바꿀수는 없었다. 에러 발생\n 해결 실질적으로는 mute 함수에서 variable을 정의해서 넣어주는 것이기 때문에 그렇게 꼼수해결법도 아니다.","fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"},"frontmatter":{"categories":"react react-query troubleshooting","title":"React Query useMutation에서 variable 옵셔널하게 사용하기","date":"February 15, 2023","draft":false}},"next":{"fields":{"slug":"/react/react-query-basic/"}},"previous":{"fields":{"slug":"/react/recoil-introduction/"}}},{"node":{"id":"e1211b4f-4197-5605-879f-45d011d0d210","excerpt":"Introduction 이 글을 읽기전 React의 상태관리 종류 4가지를 먼저 읽으면 이해가 쉬울 수 있다. 얼마전 회사 프로젝트에 react-query를 도입하여 사용중인데 정말 너무너무 좋다. react-query를 이용해서 server state를 분리하다보니 loading 처리나, refetch 등 불필요한 코드가 많이 줄었다. 뭐든지 처음이 어렵지 큰 그림을 알고있으면 도입하여 적용하는데에는 큰 문제가 없고, 사용하면서 상황에 맞게 옵션과 아키텍쳐를 변경하면 된다고 생각하기 때문에, 이번 포스팅에서는 react-query를 적용하기 위해 필요한 전반적인 개념을 정리해보고자 한다. 용도 react-query는 서버 데이터 관리를 위한 라이브러리다. 이 라이브러리는 API 호출, 캐시 및 상태 관리, 오류 처리 등의 작업을 간단하게 처리할 수 있도록 도와준다. 서버 데이터 관리는 중요한 부분 중 하나다. 클라이언트에서는 서버로부터 데이터를 가져와(Read) 화면에 그려주고…","fields":{"slug":"/react/react-query-basic/"},"frontmatter":{"categories":"react react-query","title":"React Query 시작하기 (feat. Tanstack)","date":"February 05, 2023","draft":false}},"next":{"fields":{"slug":"/react/react-state-management/"}},"previous":{"fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"}}},{"node":{"id":"afca1d77-1bf9-59de-8227-74dfb0b19eed","excerpt":"Introduction react의 상태관리 종류 4가지를 알아보고, 각각의 종류별로 왜 다르게 관리해야하는지와 React에서 제공하는 API 혹은 어떤 라이브러리를 사용해서 관리하면 좋을지 알아보도록 하자. 상태관리 종류 Local state Global state App 어느곳(여러 컴포넌트)에서 state를 확인하거나 업데이트하기 위해서 필요함. 예: authenticated user state - logged in/out Server state 서버로 받은 데이터가 UI 상태와 통합되어야 하는 경우 필요함. 예: loading, error… 대표 라이브러리: SWR, react-query URL state URL에 존재하는 데이터(pathname, query params..) 1) Local state useState useReducer useReducer vs useState 차이 useState는 하나의 state operation만 가능했다면, useReducer는 여…","fields":{"slug":"/react/react-state-management/"},"frontmatter":{"categories":"react","title":"React의 상태관리 종류 4가지","date":"February 03, 2023","draft":false}},"next":{"fields":{"slug":"/react/error-handle/"}},"previous":{"fields":{"slug":"/react/react-query-basic/"}}},{"node":{"id":"0e971c95-1f7f-53c0-b77f-9ed5af3af6e0","excerpt":"고민 혹시 제가 내린 결론에 다른 의견을 주신다면 매우 감사합니다. 🙇🏻‍♀️ 에러를 발생 vs 사전 확인을 통해 예외 처리 어느 부분까지 사전 확인이 필요한 걸까? 고민하게 된 배경 함수의 매개변수가 의도한 것과 다른 값이 들어왔을 때 에러가 발생할 수 있다. 그런데 문득 에러를 발생하게 하는 게 올바른 것인가? 아니면 애초에 에러가 발생할 상황을 하나도 빠짐없이 막는 것이 올바른 것인가? 고민이 들었다. 위의 코드를 보면 event 객체가 무조건 존재할 상황을 가졍하고 있으므로 만약 비정상적인 event 객체가 첫 번째 인자로 전달된다면 에러가 발생할 것이다. 그렇다면 이렇게 코드를 짜는 것이 좋을까? 애초에 event 객체가 존재하지 않을 수 있는 케이스를 고려해서 절대로 에러를 발생하지 않게 하는 것이 좋을까? 결론 App을 Crashed 시키지 않는 한해서 에러가 발생해야 하는 경우는 발생하게 내버려 두고, 예상할 수 있는 예외는 try…catch를 이용하여 에러가 전파되지 않도…","fields":{"slug":"/react/error-handle/"},"frontmatter":{"categories":"react","title":"에러 핸들링에 대한 고민 (feat. React)","date":"October 16, 2022","draft":false}},"next":{"fields":{"slug":"/browser/touch-mouse-event/"}},"previous":{"fields":{"slug":"/react/react-state-management/"}}}]}}, + "result": {"pageContext":{"currentCategory":"react","categories":["All","framework","react","next","translate","typescript","browser","infra","a11y","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"909b8521-890b-5dc7-9c04-3289e2502910","excerpt":"process.env 값을 구조 분해 할당 하셨나요? 우리가 환경변수를 접근할 때 객체의 속성값에 접근할 때 사용하는 점 표기법을 사용하기 때문에 구조 분해 할당을 자연스럽게 사용해도 될 것 같은데요. 이런 경우, 왜 에러가 발생하는지 이번 글을 통해 알아보겠습니다. 문제 상황 다음과 같은 코드가 있다고 가정해 봅시다. 위 코드는 process.env 객체에서 API_KEY와 NEXT_PUBLIC_ANALYTICS_ID 변수를 구조 분해 할당하여 사용하려고 합니다. 그러나 Next.js에서는 이 코드가 제대로 작동하지 않을 수 있고, 오류가 발생할 수 있습니다. 왜 이런 문제가 발생할까요? process.env는 일반적인 자바스크립트 객체와는 다르기 때문입니다. 이는 Node.js 환경에서 환경 변수를 담고 있는 특수한 객체로, 모든 속성이 문자열 형태로 저장됩니다. Next.js는 빌드 타임과 런타임에서 환경 변수에 접근하는 방식이 다르므로, 이로 인해 구조 분해 할당을 사용할 …","fields":{"slug":"/react/process-env-destructuring-error.md/"},"frontmatter":{"categories":"react next","title":"환경 변수(process.env)를 구조 분해 할당하면 안되는 이유","date":"June 26, 2024","draft":false}},"next":{"fields":{"slug":"/typescript/typescript-as-const/"}},"previous":{"fields":{"slug":"/astro-hydration/"}}},{"node":{"id":"f5dbfa6e-986a-5edb-9bc0-2f30132ce702","excerpt":"원글: https://aralroca.com/blog/html-node-streaming 썸네일 최근 몇 년간 브라우저는 HTML과 자바스크립트를 스트리밍을 지원하기 시작했습니다. 이 글에서는 이에 대한 장점과 브라우저가 자동으로 수행하지 않는 다른 작업들을 통해 스트리밍의 이점을 최대한 활용할 수 있는 방법에 대해 이야기할 것입니다. 스트리밍 HTML 초기 로드하는 동안에는 브라우저가 자동으로 처리하기 때문에 크게 신경 쓸 필요가 없습니다. 스트리밍 하는 동안 HTML 청크를 받으면, 브라우저는 그 콘텐츠를 출력합니다. 서버에서 스트리밍을 활성화하려면 헤더를 조정해야 합니다. 아래는 예시입니다. 그리고 응답에서는 ReadableStream을 사용합니다. 이것은 Bun을 사용한 예시입니다. enqueue 안에 있는 각 문자열은 브라우저가 받게 될 청크입니다. 스트리밍 중 HTML 콘텐츠 변경 성능상의 이점이 많기 때문에 많이 사용되는 방법 중 하나는 스트리밍 되는 동안 HTML 콘…","fields":{"slug":"/react/html-node-streaming/"},"frontmatter":{"categories":"translate react","title":"(번역) 스트리밍 HTML과 DOM 비교 알고리즘","date":"March 21, 2024","draft":false}},"next":{"fields":{"slug":"/browser/browser-coordinate-size-api/"}},"previous":{"fields":{"slug":"/typescript/as-const-vs-satisfies/"}}},{"node":{"id":"5fc01a99-7197-518c-a9de-60ace2976502","excerpt":"원문: https://storybook.js.org/blog/storybook-react-server-components/ 스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용 리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다. 가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다. 이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 …","fields":{"slug":"/react/storybook-react-server-components/"},"frontmatter":{"categories":"translate react","title":"(번역) 리액트 서버 컴포넌트를 위한 스토리북","date":"February 02, 2024","draft":false}},"next":{"fields":{"slug":"/react/next-app-router-react-query/"}},"previous":{"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}}},{"node":{"id":"161feee4-18f2-5161-8900-a9bc5eda4367","excerpt":"지난 글에서 react-query의 hydrate, dehydrate을 통해 서버에서 prefetching 한 데이터 사용하는 방법에 대해서 살펴보았습니다. 서버에서 prefetching 한 데이터 사용하기 오늘은 조금 실용적으로 Next.js 13, 14 버전의 app router에서 react-query를 어떻게 사용하고 세팅하면 좋을지 고민했던 내용에 대해서 이야기해보도록 하겠습니다. 해당 글은 23년 10월에 메이저 버전 업데이트된, Next.js 14와 React-Query 5 를 기준으로 작성된 글입니다. 예시로 보여주는 코드는 저의 next-14-react-query repo에서 확인 할 수 있습니다. 제가 크게 고민했던 문제는 아래 세 가지입니다. Hydrate vs ReactQueryStreamedHydration 두 가지 API 중에 어떤 것을 채택할까? Hydration API 사용 시에 RSC, RCC 모두에서 깔끔한 코드를 유지하려면 queryOption은 …","fields":{"slug":"/react/next-app-router-react-query/"},"frontmatter":{"categories":"react react-query","title":"Next.js app router에서 React Query 사용하면서 고민했던 것들","date":"January 07, 2024","draft":false}},"next":{"fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"}},"previous":{"fields":{"slug":"/react/storybook-react-server-components/"}}},{"node":{"id":"d77b67b4-3ea0-53b0-b513-8a24dd4da08d","excerpt":"Next.js나 Remix 같은 프레임워크 내에서 React-Query를 사용한다면, 서버 렌더링 될 때 요청 후 응답받은 데이터를 SPA 방식으로 전환되고 나서도 유지할 수 있을까요? 어떻게 가능할까요? React Query의 hydrate와 dehydrate는 서버에서 미리 가져온 데이터를 클라이언트 사이드에서 재사용 할 수 있게 해줍니다. 이번 글을 통해 서버 렌더링과 어떻게 이를 가능하게 하는지 hydrate와 dehydrate에 대해서 알아봅시다. Server Rendering 서버 렌더링은 사용자가 페이지를 로드하는 즉시 볼 수 있는 초기 HTML을 서버에서 생성하는 행위입니다. 이는 페이지 요청 시 즉시 발생할 수 있으며(SSR), 이전 요청이 캐시 되었거나 빌드 시간에 미리 생성(SSG) 할 수도 있습니다. 클라이언트 렌더링 애플리케이션에서는 사용자에게 화면에 콘텐츠를 표시하기 전에 최소 3번의 서버 왕복(roundtrips)이 필요합니다. 서버 렌더링은 위의 과정…","fields":{"slug":"/react/server-rendering-and-react-query/"},"frontmatter":{"categories":"react react-query","title":"서버에서 React Query prefetching 한 데이터 사용하기","date":"December 10, 2023","draft":false}},"next":{"fields":{"slug":"/react/introducing-signals/"}},"previous":{"fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"}}},{"node":{"id":"9c6b2391-e371-545b-b36b-85a35b38a7ac","excerpt":"원문: https://preactjs.com/blog/introducing-signals/ 시그널은 앱이 복잡해져도 빠른 속도를 유지하도록 하는 상태 표현 방식입니다. 시그널은 반응형 원칙에 기반을 두고 있으며, 가상 돔에 최적화된 독특한 구현을 통해 개발자에게 훌륭한 경험을 제공합니다. 본질적으로 시그널은 특정 값을 가지고 있는 .value 속성을 가진 객체입니다. 컴포넌트 내에서 시그널의 value 속성에 접근하면, 그 시그널의 값이 변경될 때 해당 컴포넌트가 자동으로 업데이트됩니다. 이는 간단하고 작성하기 쉬울 뿐만 아니라, 앱이 얼마나 많은 컴포넌트를 가지고 있든 상태 업데이트가 빠르게 유지되도록 보장합니다. 시그널은 기본적으로 빠르며, 백그라운드에서 자동으로 업데이트를 최적화해줍니다. REPL에서 실행 시그널은 훅과 달리 컴포넌트 내부 또는 외부에서 사용할 수 있습니다. 또한 시그널은 훅과 클래스 컴포넌트 모두에서 훌륭하게 작동하므로, 기존 지식을 활용하며 자신의 속도에 …","fields":{"slug":"/react/introducing-signals/"},"frontmatter":{"categories":"translate react","title":"(번역) 시그널(Signal)에 대한 소개","date":"November 27, 2023","draft":false}},"next":{"fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"}},"previous":{"fields":{"slug":"/react/server-rendering-and-react-query/"}}},{"node":{"id":"5f49b3c9-9991-5b3e-9e49-46ec9a85c02d","excerpt":"원글: https://itnext.io/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components-82e46b5820c 프런트엔드 개발 영역에서는 용어와 패러다임이 때로는 이해하기 어려울 수 있으며 ‘헤드리스 UI’ 또는 ‘헤드리스 컴포넌트’도 이 범주에 속할 수 있습니다. 이러한 용어들이 무엇을 의미하는지 궁금해서 고개를 갸웃거리고, 혼자만 그런 것이 아닙니다. 사실, 혼란스러운 이름에도 불구하고 이러한 개념들은 복잡한 사용자 인터페이스 관리를 상당히 단순화할 수 있는 매력적인 전략입니다. 헤드리스 컴포넌트는 난해해 보일 수 있지만, 그 진정한 힘은 유연성, 재사용 가능성, 그리고 코드베이스의 구성과 깔끔함을 향상시킬 수 있는 능력에 있습니다. 이 글에서는 이 패턴이 정확히 무엇인지, 왜 유용한지, 그리고 인터페이스 디자인에 대한 접근 방식을 어떻게 혁신할 수 있는지에 대해 탐구해 볼 것입니다. …","fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"},"frontmatter":{"categories":"translate react","title":"(번역) React에서 UI와 로직 분리하기:헤드리스 컴포넌트를 사용한 클린 코드 접근법","date":"September 13, 2023","draft":false}},"next":{"fields":{"slug":"/cs/design-patterns-for-humans/"}},"previous":{"fields":{"slug":"/react/introducing-signals/"}}},{"node":{"id":"1a5d4b16-06aa-572e-802e-98a3de358758","excerpt":"원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …","fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"},"frontmatter":{"categories":"translate react react-query","title":"(번역) React Query를 사용하여 서버 상태를 관리하는 방법","date":"June 21, 2023","draft":false}},"next":{"fields":{"slug":"/cs/6-caching-strategies/"}},"previous":{"fields":{"slug":"/javascript/garbage-collection/"}}},{"node":{"id":"08e40669-d8eb-531c-ab48-fcad02ba39ef","excerpt":"React 18로 버전업 하면서 프로젝트에 Recoil을 도입했는데, 그 과정에서 고민했던 내용을 기록차 남겨본다.\n TL;DR; 개념 정리나 장단점 비교에 대해서는 이미 알고 계신분들도 있을 것 같아서 폴더구조나 Redux를 대체하는게 맞는가에 대해서만 먼저 간단하게 요약한 내용을 공유한다. 1. 폴더 구조는 data source 별로 나누자 Suggested folder structure for atoms and selectors recoil 폴더 하위에 data source 별로 나누자 data source 하위에 atoms, selectors, hooks를 두자 2. concurrency mode 사용하려면 Redux는 버리고 Recoil로 넘어가야하나? useSyncExternalStore hook 을 사용해서 Redux에서도 동시성 모드를 사용할 수 있다. 이 부분에 대해서 고민해 본 내용은 아래에.. Recoil 도입 배경 Recoil은 Facebook에서 개발한 상태…","fields":{"slug":"/react/recoil-introduction/"},"frontmatter":{"categories":"react","title":"Recoil 도입기(feat. 폴더구조)","date":"April 08, 2023","draft":false}},"next":{"fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"}},"previous":{"fields":{"slug":"/feature/input-date/"}}},{"node":{"id":"e19abd3b-8537-528d-a31e-3a74ebbb5cc5","excerpt":"Introduction #1077 이슈를 보면 mutateFn의 variable을 필수값으로 변경한 이력을 볼 수 있다. mutation인데 왜 variable이 없냐고? 나의 경우에는 URL의 있는 queryParams를 읽어와서 데이터를 넘겨주면 되기 때문에 useSomething hook 안에서 처리하고 싶었다. 문제상황 mutate() 와 같이 variable에 아무 값도 넣지 않고 mutation 함수를 호출 할 수 없다.\nTVariables 타입에 undefined도 줘보고 이래저래 별짓을 다해봐도 필수값을 optional 하게 바꿀수는 없었다. 에러 발생\n 해결 실질적으로는 mute 함수에서 variable을 정의해서 넣어주는 것이기 때문에 그렇게 꼼수해결법도 아니다.","fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"},"frontmatter":{"categories":"react react-query troubleshooting","title":"React Query useMutation에서 variable 옵셔널하게 사용하기","date":"February 15, 2023","draft":false}},"next":{"fields":{"slug":"/react/react-query-basic/"}},"previous":{"fields":{"slug":"/react/recoil-introduction/"}}},{"node":{"id":"e1211b4f-4197-5605-879f-45d011d0d210","excerpt":"Introduction 이 글을 읽기전 React의 상태관리 종류 4가지를 먼저 읽으면 이해가 쉬울 수 있다. 얼마전 회사 프로젝트에 react-query를 도입하여 사용중인데 정말 너무너무 좋다. react-query를 이용해서 server state를 분리하다보니 loading 처리나, refetch 등 불필요한 코드가 많이 줄었다. 뭐든지 처음이 어렵지 큰 그림을 알고있으면 도입하여 적용하는데에는 큰 문제가 없고, 사용하면서 상황에 맞게 옵션과 아키텍쳐를 변경하면 된다고 생각하기 때문에, 이번 포스팅에서는 react-query를 적용하기 위해 필요한 전반적인 개념을 정리해보고자 한다. 용도 react-query는 서버 데이터 관리를 위한 라이브러리다. 이 라이브러리는 API 호출, 캐시 및 상태 관리, 오류 처리 등의 작업을 간단하게 처리할 수 있도록 도와준다. 서버 데이터 관리는 중요한 부분 중 하나다. 클라이언트에서는 서버로부터 데이터를 가져와(Read) 화면에 그려주고…","fields":{"slug":"/react/react-query-basic/"},"frontmatter":{"categories":"react react-query","title":"React Query 시작하기 (feat. Tanstack)","date":"February 05, 2023","draft":false}},"next":{"fields":{"slug":"/react/react-state-management/"}},"previous":{"fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"}}},{"node":{"id":"afca1d77-1bf9-59de-8227-74dfb0b19eed","excerpt":"Introduction react의 상태관리 종류 4가지를 알아보고, 각각의 종류별로 왜 다르게 관리해야하는지와 React에서 제공하는 API 혹은 어떤 라이브러리를 사용해서 관리하면 좋을지 알아보도록 하자. 상태관리 종류 Local state Global state App 어느곳(여러 컴포넌트)에서 state를 확인하거나 업데이트하기 위해서 필요함. 예: authenticated user state - logged in/out Server state 서버로 받은 데이터가 UI 상태와 통합되어야 하는 경우 필요함. 예: loading, error… 대표 라이브러리: SWR, react-query URL state URL에 존재하는 데이터(pathname, query params..) 1) Local state useState useReducer useReducer vs useState 차이 useState는 하나의 state operation만 가능했다면, useReducer는 여…","fields":{"slug":"/react/react-state-management/"},"frontmatter":{"categories":"react","title":"React의 상태관리 종류 4가지","date":"February 03, 2023","draft":false}},"next":{"fields":{"slug":"/react/error-handle/"}},"previous":{"fields":{"slug":"/react/react-query-basic/"}}},{"node":{"id":"0e971c95-1f7f-53c0-b77f-9ed5af3af6e0","excerpt":"고민 혹시 제가 내린 결론에 다른 의견을 주신다면 매우 감사합니다. 🙇🏻‍♀️ 에러를 발생 vs 사전 확인을 통해 예외 처리 어느 부분까지 사전 확인이 필요한 걸까? 고민하게 된 배경 함수의 매개변수가 의도한 것과 다른 값이 들어왔을 때 에러가 발생할 수 있다. 그런데 문득 에러를 발생하게 하는 게 올바른 것인가? 아니면 애초에 에러가 발생할 상황을 하나도 빠짐없이 막는 것이 올바른 것인가? 고민이 들었다. 위의 코드를 보면 event 객체가 무조건 존재할 상황을 가졍하고 있으므로 만약 비정상적인 event 객체가 첫 번째 인자로 전달된다면 에러가 발생할 것이다. 그렇다면 이렇게 코드를 짜는 것이 좋을까? 애초에 event 객체가 존재하지 않을 수 있는 케이스를 고려해서 절대로 에러를 발생하지 않게 하는 것이 좋을까? 결론 App을 Crashed 시키지 않는 한해서 에러가 발생해야 하는 경우는 발생하게 내버려 두고, 예상할 수 있는 예외는 try…catch를 이용하여 에러가 전파되지 않도…","fields":{"slug":"/react/error-handle/"},"frontmatter":{"categories":"react","title":"에러 핸들링에 대한 고민 (feat. React)","date":"October 16, 2022","draft":false}},"next":{"fields":{"slug":"/browser/touch-mouse-event/"}},"previous":{"fields":{"slug":"/react/react-state-management/"}}}]}}, "staticQueryHashes": ["1073350324","1956554647","2938748437"]} \ No newline at end of file diff --git a/page-data/posts/test/page-data.json b/page-data/posts/test/page-data.json index bc0ff5ca..a8ce150e 100644 --- a/page-data/posts/test/page-data.json +++ b/page-data/posts/test/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-category-template-js", "path": "/posts/test", - "result": {"pageContext":{"currentCategory":"test","categories":["All","framework","react","next","translate","typescript","browser","a11y","infra","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"34e53d64-0467-56f3-8179-68e3f4d6c48c","excerpt":"🔨 다듬을 필요가 있는 글입니다. 테스트 flow 테스트 코드 작성 → 테스트 읽고 → 실행 → 출력(Reporter) 테스트 종류 단위 테스트: 모듈을 분리된 환경에서 테스트 의존성이 있는 모듈을 제어하기 위해 필연적으로 모의 객체(Mocking)을 사용할 수밖에 없으며, 이 경우 각 모듈이 실제로 잘 연결되어 상호 작용하는지에 대해서는 검증하지 못한다. 통합 테스트: 2개 이상의 모듈 간의 상호작용을 테스트 (개발자 관점의 테스트) 단위 테스트에 비해 mocking을 덜하며, 모듈 간의 연결에서 발생하는 에러를 검증할 수 있다. E2E 테스트: 사용자의 실행 환경과 거의 동일한 환경에서 테스트를 진행 (사용자 관점의 테스트) 세부 모듈들이 갖는 다양한 상황들의 조합을 고려해야 하기 때문에 테스트 자바스크립트 테스트 도구 자바스크립트 테스트는 도구들은 아래와 같이 분류할 수 있다. Test Runners Testing Frameworks Assertion Libraries Tes…","fields":{"slug":"/test/test-introduction/"},"frontmatter":{"categories":"test","title":"자바스크립트 테스트 개념정리","date":"April 14, 2022","draft":false}},"next":null,"previous":{"fields":{"slug":"/feature/confirm/"}}}]}}, + "result": {"pageContext":{"currentCategory":"test","categories":["All","framework","react","next","translate","typescript","browser","infra","a11y","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"34e53d64-0467-56f3-8179-68e3f4d6c48c","excerpt":"🔨 다듬을 필요가 있는 글입니다. 테스트 flow 테스트 코드 작성 → 테스트 읽고 → 실행 → 출력(Reporter) 테스트 종류 단위 테스트: 모듈을 분리된 환경에서 테스트 의존성이 있는 모듈을 제어하기 위해 필연적으로 모의 객체(Mocking)을 사용할 수밖에 없으며, 이 경우 각 모듈이 실제로 잘 연결되어 상호 작용하는지에 대해서는 검증하지 못한다. 통합 테스트: 2개 이상의 모듈 간의 상호작용을 테스트 (개발자 관점의 테스트) 단위 테스트에 비해 mocking을 덜하며, 모듈 간의 연결에서 발생하는 에러를 검증할 수 있다. E2E 테스트: 사용자의 실행 환경과 거의 동일한 환경에서 테스트를 진행 (사용자 관점의 테스트) 세부 모듈들이 갖는 다양한 상황들의 조합을 고려해야 하기 때문에 테스트 자바스크립트 테스트 도구 자바스크립트 테스트는 도구들은 아래와 같이 분류할 수 있다. Test Runners Testing Frameworks Assertion Libraries Tes…","fields":{"slug":"/test/test-introduction/"},"frontmatter":{"categories":"test","title":"자바스크립트 테스트 개념정리","date":"April 14, 2022","draft":false}},"next":null,"previous":{"fields":{"slug":"/feature/confirm/"}}}]}}, "staticQueryHashes": ["1073350324","1956554647","2938748437"]} \ No newline at end of file diff --git a/page-data/posts/translate/page-data.json b/page-data/posts/translate/page-data.json index dbce91a4..2e4ac893 100644 --- a/page-data/posts/translate/page-data.json +++ b/page-data/posts/translate/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-category-template-js", "path": "/posts/translate", - "result": {"pageContext":{"currentCategory":"translate","categories":["All","framework","react","next","translate","typescript","browser","a11y","infra","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"7af1f27b-4f32-56a8-8140-5191c8ba3150","excerpt":"원글: https://www.omarileon.me/blog/typescript-as-const 새로운 구문인 “상수 어설션(const assertions)“은 TypeScript 3.4에서 도입되었습니다. 이것은 변수가 변경되지 않을 것(immutable)이고 가능한 엄격한 타입을 제공해야 한다는 것을 타입스크립트에 알려줍니다. 이는 타입의 종류에 따라 다르게 영향을 미치므로, 이 글에서는 상수 어설션을 어떻게 사용하고 왜 유용한지에 대해 설명할 것입니다. 문자열/숫자 문자열이나 숫자에 “as const”를 추가하면 특정 값으로 타입을 좁힐 수 있습니다. 숫자의 경우 문자열 또는 숫자의 경우 일반적으로 “const”를 사용하여 변수를 정의하면 동일한 효과를 얻을 수 있기 때문에 덜 유용합니다. 런타임 안전성이라는 추가 이점도 있습니다. 때로는 변수로 값을 정의하지 않고, 단순히 문자열 리터럴을 사용하고 싶을 수 있습니다. 예를 들어 값을 반환할 때처럼요. 그때 “as const”…","fields":{"slug":"/typescript/typescript-as-const/"},"frontmatter":{"categories":"translate typescript","title":"(번역) 타입스크립트에서 'As Const' 이해하기","date":"April 14, 2024","draft":false}},"next":{"fields":{"slug":"/typescript/as-const-vs-satisfies/"}},"previous":{"fields":{"slug":"/react/process-env-destructuring-error.md/"}}},{"node":{"id":"f5dbfa6e-986a-5edb-9bc0-2f30132ce702","excerpt":"원글: https://aralroca.com/blog/html-node-streaming 썸네일 최근 몇 년간 브라우저는 HTML과 자바스크립트를 스트리밍을 지원하기 시작했습니다. 이 글에서는 이에 대한 장점과 브라우저가 자동으로 수행하지 않는 다른 작업들을 통해 스트리밍의 이점을 최대한 활용할 수 있는 방법에 대해 이야기할 것입니다. 스트리밍 HTML 초기 로드하는 동안에는 브라우저가 자동으로 처리하기 때문에 크게 신경 쓸 필요가 없습니다. 스트리밍 하는 동안 HTML 청크를 받으면, 브라우저는 그 콘텐츠를 출력합니다. 서버에서 스트리밍을 활성화하려면 헤더를 조정해야 합니다. 아래는 예시입니다. 그리고 응답에서는 ReadableStream을 사용합니다. 이것은 Bun을 사용한 예시입니다. enqueue 안에 있는 각 문자열은 브라우저가 받게 될 청크입니다. 스트리밍 중 HTML 콘텐츠 변경 성능상의 이점이 많기 때문에 많이 사용되는 방법 중 하나는 스트리밍 되는 동안 HTML 콘…","fields":{"slug":"/react/html-node-streaming/"},"frontmatter":{"categories":"translate react","title":"(번역) 스트리밍 HTML과 DOM 비교 알고리즘","date":"March 21, 2024","draft":false}},"next":{"fields":{"slug":"/browser/browser-coordinate-size-api/"}},"previous":{"fields":{"slug":"/typescript/as-const-vs-satisfies/"}}},{"node":{"id":"49246cb3-c4b8-5aeb-a4c5-a1b737e89b51","excerpt":"원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939 CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다. 이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다. 읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다. 약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리…","fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"},"frontmatter":{"categories":"translate a11y","title":"(번역) 접근성을 고려하여 CSS 작성하기","date":"February 18, 2024","draft":false}},"next":{"fields":{"slug":"/infra/docker-command/"}},"previous":{"fields":{"slug":"/browser/browser-coordinate-size-api/"}}},{"node":{"id":"5fc01a99-7197-518c-a9de-60ace2976502","excerpt":"원문: https://storybook.js.org/blog/storybook-react-server-components/ 스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용 리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다. 가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다. 이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 …","fields":{"slug":"/react/storybook-react-server-components/"},"frontmatter":{"categories":"translate react","title":"(번역) 리액트 서버 컴포넌트를 위한 스토리북","date":"February 02, 2024","draft":false}},"next":{"fields":{"slug":"/react/next-app-router-react-query/"}},"previous":{"fields":{"slug":"/infra/docker-command/"}}},{"node":{"id":"55331269-8b15-516d-aa6d-7eb90d2fedee","excerpt":"원문: https://javascript.plainenglish.io/the-5-most-innovative-proposals-for-javascript-planned-for-2024-22139dd2f546 JavaScript Logo 2024 자바스크립트가 빠른 속도로 발전함에 따라 2024년은 이 프로그래밍 언어에 있어 중요한 해가 될 것입니다. 이 글에서는 개발자들이 자바스크립트 코딩에 접근하는 방식을 재정의하기 위한 최신 제안을 자세히 살펴보겠습니다. 이 글이 유용하길 바랍니다! 시작해 봅시다! 목차 데코레이터 Temporal API 파이프라인 연산자 Error cause 레코드와 튜플 1. 데코레이터 자바스크립트에 데코레이터가 도입된 것은 개발자가 클래스, 메서드, 프로퍼티 및 매개변수의 동작을 조작하고 개선할 수 있는 방법이 크게 발전했음을 의미합니다. 파이썬과 타입스크립트와 같은 다른 프로그래밍 언어에서 유래한 데코레이터는 기능을 추가하거나 수정하는 간결하고 선언적인…","fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"},"frontmatter":{"categories":"translate javascript","title":"(번역) 2024년 자바스크립트의 5가지 가장 혁신적인 제안","date":"December 20, 2023","draft":false}},"next":{"fields":{"slug":"/react/server-rendering-and-react-query/"}},"previous":{"fields":{"slug":"/react/next-app-router-react-query/"}}},{"node":{"id":"9c6b2391-e371-545b-b36b-85a35b38a7ac","excerpt":"원문: https://preactjs.com/blog/introducing-signals/ 시그널은 앱이 복잡해져도 빠른 속도를 유지하도록 하는 상태 표현 방식입니다. 시그널은 반응형 원칙에 기반을 두고 있으며, 가상 돔에 최적화된 독특한 구현을 통해 개발자에게 훌륭한 경험을 제공합니다. 본질적으로 시그널은 특정 값을 가지고 있는 .value 속성을 가진 객체입니다. 컴포넌트 내에서 시그널의 value 속성에 접근하면, 그 시그널의 값이 변경될 때 해당 컴포넌트가 자동으로 업데이트됩니다. 이는 간단하고 작성하기 쉬울 뿐만 아니라, 앱이 얼마나 많은 컴포넌트를 가지고 있든 상태 업데이트가 빠르게 유지되도록 보장합니다. 시그널은 기본적으로 빠르며, 백그라운드에서 자동으로 업데이트를 최적화해줍니다. REPL에서 실행 시그널은 훅과 달리 컴포넌트 내부 또는 외부에서 사용할 수 있습니다. 또한 시그널은 훅과 클래스 컴포넌트 모두에서 훌륭하게 작동하므로, 기존 지식을 활용하며 자신의 속도에 …","fields":{"slug":"/react/introducing-signals/"},"frontmatter":{"categories":"translate react","title":"(번역) 시그널(Signal)에 대한 소개","date":"November 27, 2023","draft":false}},"next":{"fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"}},"previous":{"fields":{"slug":"/react/server-rendering-and-react-query/"}}},{"node":{"id":"5f49b3c9-9991-5b3e-9e49-46ec9a85c02d","excerpt":"원글: https://itnext.io/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components-82e46b5820c 프런트엔드 개발 영역에서는 용어와 패러다임이 때로는 이해하기 어려울 수 있으며 ‘헤드리스 UI’ 또는 ‘헤드리스 컴포넌트’도 이 범주에 속할 수 있습니다. 이러한 용어들이 무엇을 의미하는지 궁금해서 고개를 갸웃거리고, 혼자만 그런 것이 아닙니다. 사실, 혼란스러운 이름에도 불구하고 이러한 개념들은 복잡한 사용자 인터페이스 관리를 상당히 단순화할 수 있는 매력적인 전략입니다. 헤드리스 컴포넌트는 난해해 보일 수 있지만, 그 진정한 힘은 유연성, 재사용 가능성, 그리고 코드베이스의 구성과 깔끔함을 향상시킬 수 있는 능력에 있습니다. 이 글에서는 이 패턴이 정확히 무엇인지, 왜 유용한지, 그리고 인터페이스 디자인에 대한 접근 방식을 어떻게 혁신할 수 있는지에 대해 탐구해 볼 것입니다. …","fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"},"frontmatter":{"categories":"translate react","title":"(번역) React에서 UI와 로직 분리하기:헤드리스 컴포넌트를 사용한 클린 코드 접근법","date":"September 13, 2023","draft":false}},"next":{"fields":{"slug":"/cs/design-patterns-for-humans/"}},"previous":{"fields":{"slug":"/react/introducing-signals/"}}},{"node":{"id":"44fd3704-304a-5b2a-8702-af196e247d28","excerpt":"원글: https://github.com/kamranahmedse/design-patterns-for-humans/blob/master/readme.md 역자주: 이 글은 Kamran Ahmed의 글이며, PHP-7 기준으로 작성된 예제 코드를 타입스크립트로 변경하였습니다. logo 🎉 디자인 패턴에 대한 초간단 설명입니다! 🎉 누구든지 혼란스럽게 만들 수 있는 주제입니다. 이 글에서는 최대한 간단한 방식으로 설명함으로써 당신의 기억 속 (그리고 아마도 제 기억)에 각인시키기 위해 노력할 것입니다. 저의 다른 프로젝트도 확인하고 트위터에서 “안녕”이라고 인사해 보세요. 생성 패턴 구조 패턴 행동 패턴 심플 팩토리(Simple Factory) 어댑터(Adapter) 책임 연쇄(Chain of Responsibility) 팩토리 메서드(Factory Method) 브릿지(Bridge) 명령(Command) 추상 팩토리(Abstract Factory) 컴포지트(Composite) 반복자…","fields":{"slug":"/cs/design-patterns-for-humans/"},"frontmatter":{"categories":"translate cs","title":"(번역) 우리들을 위한 디자인 패턴","date":"August 27, 2023","draft":false}},"next":{"fields":{"slug":"/javascript/garbage-collection/"}},"previous":{"fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"}}},{"node":{"id":"1a5d4b16-06aa-572e-802e-98a3de358758","excerpt":"원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …","fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"},"frontmatter":{"categories":"translate react react-query","title":"(번역) React Query를 사용하여 서버 상태를 관리하는 방법","date":"June 21, 2023","draft":false}},"next":{"fields":{"slug":"/cs/6-caching-strategies/"}},"previous":{"fields":{"slug":"/javascript/garbage-collection/"}}},{"node":{"id":"872218ff-8507-5910-8001-bc52d72fca27","excerpt":"원글: https://javascript.plainenglish.io/6-caching-strategies-to-remember-while-designing-cache-system-da058a3757cf 캐시 시스템 관련 용어, 읽기 vs 쓰기 중심의 애플리케이션에서 캐싱 전략, 캐시를 무효화하는 방법 등에 대해서 알려드리겠습니다. 개요 캐싱의 목표는 원본 소스에서 데이터를 가져오는 횟수를 줄여 처리 속도를 높이고 대기 시간을 감소시키는 것입니다. 캐싱은 인메모리 캐싱, 디스크 캐싱, 데이터베이스 캐싱, CDN 캐싱과 같은 다양한 수준의 아키텍처에서 구현될 수 있습니다. 데이터는 각각 고유한 이점이 있는 다양한 기술을 사용하여 캐싱할 수 있습니다. 인메모리 캐싱은 컴퓨터의 주 메모리에 데이터를 저장하여 디스크 저장소 보다 빠른 액세스를 제공합니다. 반면 디스크 캐싱은 하드 디스크에 데이터를 저장하므로 주 메모리보다는 느리지만 원격 소스에서 데이터를 가져오는 것보다 빠릅니다. 데이터…","fields":{"slug":"/cs/6-caching-strategies/"},"frontmatter":{"categories":"translate cs","title":"(번역) 캐시 시스템 설계할 때 기억해야 할 6가지 캐싱 전략","date":"June 13, 2023","draft":false}},"next":{"fields":{"slug":"/javascript/metaprogramming/"}},"previous":{"fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"}}},{"node":{"id":"09bde05f-002e-5468-8934-6141fab54678","excerpt":"원문: https://blog.openreplay.com/deep-copying-objects-with-the-structuredclone-api/ Deep Copying Objects with the StructuredClone API 개요: 자바스크립트에서 객체를 복사하는 것은 간단하지 않으며, 이는 잘 알려진 문제입니다. 그러나 이 글에서는 해결책을 제공합니다. StructuredClone API를 사용하면 모든 객체를 간단하고 빠르게 복사할 수 있습니다. 자바스크립트에서 객체가 변수에 저장될 때, 해당 변수는 객체의 참조값을 갖습니다. 이는 변수 자체에 객체를 저장하는 것이 아니라, 객체의 메모리 위치를 나타내는 식별자를 저장한다는 것을 의미합니다. 객체의 복사는 원시 타입과는 다른 방식으로 동작합니다. 얕은 복사(Shallow Copy) vs. 깊은 복사(Deep Copy) 자바스크립트에서 값은 두 가지 방법으로 복사할 수 있습니다. 얕은 복사와 깊은 복사입니다. 얕은 복…","fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"},"frontmatter":{"categories":"translate javascript","title":"(번역) StructuredClone API를 사용하여 객체를 깊은 복사하는 법","date":"May 07, 2023","draft":false}},"next":{"fields":{"slug":"/feature/input-date/"}},"previous":{"fields":{"slug":"/javascript/metaprogramming/"}}}]}}, + "result": {"pageContext":{"currentCategory":"translate","categories":["All","framework","react","next","translate","typescript","browser","infra","a11y","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"7af1f27b-4f32-56a8-8140-5191c8ba3150","excerpt":"원글: https://www.omarileon.me/blog/typescript-as-const 새로운 구문인 “상수 어설션(const assertions)“은 TypeScript 3.4에서 도입되었습니다. 이것은 변수가 변경되지 않을 것(immutable)이고 가능한 엄격한 타입을 제공해야 한다는 것을 타입스크립트에 알려줍니다. 이는 타입의 종류에 따라 다르게 영향을 미치므로, 이 글에서는 상수 어설션을 어떻게 사용하고 왜 유용한지에 대해 설명할 것입니다. 문자열/숫자 문자열이나 숫자에 “as const”를 추가하면 특정 값으로 타입을 좁힐 수 있습니다. 숫자의 경우 문자열 또는 숫자의 경우 일반적으로 “const”를 사용하여 변수를 정의하면 동일한 효과를 얻을 수 있기 때문에 덜 유용합니다. 런타임 안전성이라는 추가 이점도 있습니다. 때로는 변수로 값을 정의하지 않고, 단순히 문자열 리터럴을 사용하고 싶을 수 있습니다. 예를 들어 값을 반환할 때처럼요. 그때 “as const”…","fields":{"slug":"/typescript/typescript-as-const/"},"frontmatter":{"categories":"translate typescript","title":"(번역) 타입스크립트에서 'As Const' 이해하기","date":"April 14, 2024","draft":false}},"next":{"fields":{"slug":"/typescript/as-const-vs-satisfies/"}},"previous":{"fields":{"slug":"/react/process-env-destructuring-error.md/"}}},{"node":{"id":"f5dbfa6e-986a-5edb-9bc0-2f30132ce702","excerpt":"원글: https://aralroca.com/blog/html-node-streaming 썸네일 최근 몇 년간 브라우저는 HTML과 자바스크립트를 스트리밍을 지원하기 시작했습니다. 이 글에서는 이에 대한 장점과 브라우저가 자동으로 수행하지 않는 다른 작업들을 통해 스트리밍의 이점을 최대한 활용할 수 있는 방법에 대해 이야기할 것입니다. 스트리밍 HTML 초기 로드하는 동안에는 브라우저가 자동으로 처리하기 때문에 크게 신경 쓸 필요가 없습니다. 스트리밍 하는 동안 HTML 청크를 받으면, 브라우저는 그 콘텐츠를 출력합니다. 서버에서 스트리밍을 활성화하려면 헤더를 조정해야 합니다. 아래는 예시입니다. 그리고 응답에서는 ReadableStream을 사용합니다. 이것은 Bun을 사용한 예시입니다. enqueue 안에 있는 각 문자열은 브라우저가 받게 될 청크입니다. 스트리밍 중 HTML 콘텐츠 변경 성능상의 이점이 많기 때문에 많이 사용되는 방법 중 하나는 스트리밍 되는 동안 HTML 콘…","fields":{"slug":"/react/html-node-streaming/"},"frontmatter":{"categories":"translate react","title":"(번역) 스트리밍 HTML과 DOM 비교 알고리즘","date":"March 21, 2024","draft":false}},"next":{"fields":{"slug":"/browser/browser-coordinate-size-api/"}},"previous":{"fields":{"slug":"/typescript/as-const-vs-satisfies/"}}},{"node":{"id":"49246cb3-c4b8-5aeb-a4c5-a1b737e89b51","excerpt":"원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939 CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다. 이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다. 읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다. 약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리…","fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"},"frontmatter":{"categories":"translate a11y","title":"(번역) 접근성을 고려하여 CSS 작성하기","date":"February 18, 2024","draft":false}},"next":{"fields":{"slug":"/react/storybook-react-server-components/"}},"previous":{"fields":{"slug":"/infra/docker-command/"}}},{"node":{"id":"5fc01a99-7197-518c-a9de-60ace2976502","excerpt":"원문: https://storybook.js.org/blog/storybook-react-server-components/ 스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용 리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다. 가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다. 이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 …","fields":{"slug":"/react/storybook-react-server-components/"},"frontmatter":{"categories":"translate react","title":"(번역) 리액트 서버 컴포넌트를 위한 스토리북","date":"February 02, 2024","draft":false}},"next":{"fields":{"slug":"/react/next-app-router-react-query/"}},"previous":{"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}}},{"node":{"id":"55331269-8b15-516d-aa6d-7eb90d2fedee","excerpt":"원문: https://javascript.plainenglish.io/the-5-most-innovative-proposals-for-javascript-planned-for-2024-22139dd2f546 JavaScript Logo 2024 자바스크립트가 빠른 속도로 발전함에 따라 2024년은 이 프로그래밍 언어에 있어 중요한 해가 될 것입니다. 이 글에서는 개발자들이 자바스크립트 코딩에 접근하는 방식을 재정의하기 위한 최신 제안을 자세히 살펴보겠습니다. 이 글이 유용하길 바랍니다! 시작해 봅시다! 목차 데코레이터 Temporal API 파이프라인 연산자 Error cause 레코드와 튜플 1. 데코레이터 자바스크립트에 데코레이터가 도입된 것은 개발자가 클래스, 메서드, 프로퍼티 및 매개변수의 동작을 조작하고 개선할 수 있는 방법이 크게 발전했음을 의미합니다. 파이썬과 타입스크립트와 같은 다른 프로그래밍 언어에서 유래한 데코레이터는 기능을 추가하거나 수정하는 간결하고 선언적인…","fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"},"frontmatter":{"categories":"translate javascript","title":"(번역) 2024년 자바스크립트의 5가지 가장 혁신적인 제안","date":"December 20, 2023","draft":false}},"next":{"fields":{"slug":"/react/server-rendering-and-react-query/"}},"previous":{"fields":{"slug":"/react/next-app-router-react-query/"}}},{"node":{"id":"9c6b2391-e371-545b-b36b-85a35b38a7ac","excerpt":"원문: https://preactjs.com/blog/introducing-signals/ 시그널은 앱이 복잡해져도 빠른 속도를 유지하도록 하는 상태 표현 방식입니다. 시그널은 반응형 원칙에 기반을 두고 있으며, 가상 돔에 최적화된 독특한 구현을 통해 개발자에게 훌륭한 경험을 제공합니다. 본질적으로 시그널은 특정 값을 가지고 있는 .value 속성을 가진 객체입니다. 컴포넌트 내에서 시그널의 value 속성에 접근하면, 그 시그널의 값이 변경될 때 해당 컴포넌트가 자동으로 업데이트됩니다. 이는 간단하고 작성하기 쉬울 뿐만 아니라, 앱이 얼마나 많은 컴포넌트를 가지고 있든 상태 업데이트가 빠르게 유지되도록 보장합니다. 시그널은 기본적으로 빠르며, 백그라운드에서 자동으로 업데이트를 최적화해줍니다. REPL에서 실행 시그널은 훅과 달리 컴포넌트 내부 또는 외부에서 사용할 수 있습니다. 또한 시그널은 훅과 클래스 컴포넌트 모두에서 훌륭하게 작동하므로, 기존 지식을 활용하며 자신의 속도에 …","fields":{"slug":"/react/introducing-signals/"},"frontmatter":{"categories":"translate react","title":"(번역) 시그널(Signal)에 대한 소개","date":"November 27, 2023","draft":false}},"next":{"fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"}},"previous":{"fields":{"slug":"/react/server-rendering-and-react-query/"}}},{"node":{"id":"5f49b3c9-9991-5b3e-9e49-46ec9a85c02d","excerpt":"원글: https://itnext.io/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components-82e46b5820c 프런트엔드 개발 영역에서는 용어와 패러다임이 때로는 이해하기 어려울 수 있으며 ‘헤드리스 UI’ 또는 ‘헤드리스 컴포넌트’도 이 범주에 속할 수 있습니다. 이러한 용어들이 무엇을 의미하는지 궁금해서 고개를 갸웃거리고, 혼자만 그런 것이 아닙니다. 사실, 혼란스러운 이름에도 불구하고 이러한 개념들은 복잡한 사용자 인터페이스 관리를 상당히 단순화할 수 있는 매력적인 전략입니다. 헤드리스 컴포넌트는 난해해 보일 수 있지만, 그 진정한 힘은 유연성, 재사용 가능성, 그리고 코드베이스의 구성과 깔끔함을 향상시킬 수 있는 능력에 있습니다. 이 글에서는 이 패턴이 정확히 무엇인지, 왜 유용한지, 그리고 인터페이스 디자인에 대한 접근 방식을 어떻게 혁신할 수 있는지에 대해 탐구해 볼 것입니다. …","fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"},"frontmatter":{"categories":"translate react","title":"(번역) React에서 UI와 로직 분리하기:헤드리스 컴포넌트를 사용한 클린 코드 접근법","date":"September 13, 2023","draft":false}},"next":{"fields":{"slug":"/cs/design-patterns-for-humans/"}},"previous":{"fields":{"slug":"/react/introducing-signals/"}}},{"node":{"id":"44fd3704-304a-5b2a-8702-af196e247d28","excerpt":"원글: https://github.com/kamranahmedse/design-patterns-for-humans/blob/master/readme.md 역자주: 이 글은 Kamran Ahmed의 글이며, PHP-7 기준으로 작성된 예제 코드를 타입스크립트로 변경하였습니다. logo 🎉 디자인 패턴에 대한 초간단 설명입니다! 🎉 누구든지 혼란스럽게 만들 수 있는 주제입니다. 이 글에서는 최대한 간단한 방식으로 설명함으로써 당신의 기억 속 (그리고 아마도 제 기억)에 각인시키기 위해 노력할 것입니다. 저의 다른 프로젝트도 확인하고 트위터에서 “안녕”이라고 인사해 보세요. 생성 패턴 구조 패턴 행동 패턴 심플 팩토리(Simple Factory) 어댑터(Adapter) 책임 연쇄(Chain of Responsibility) 팩토리 메서드(Factory Method) 브릿지(Bridge) 명령(Command) 추상 팩토리(Abstract Factory) 컴포지트(Composite) 반복자…","fields":{"slug":"/cs/design-patterns-for-humans/"},"frontmatter":{"categories":"translate cs","title":"(번역) 우리들을 위한 디자인 패턴","date":"August 27, 2023","draft":false}},"next":{"fields":{"slug":"/javascript/garbage-collection/"}},"previous":{"fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"}}},{"node":{"id":"1a5d4b16-06aa-572e-802e-98a3de358758","excerpt":"원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …","fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"},"frontmatter":{"categories":"translate react react-query","title":"(번역) React Query를 사용하여 서버 상태를 관리하는 방법","date":"June 21, 2023","draft":false}},"next":{"fields":{"slug":"/cs/6-caching-strategies/"}},"previous":{"fields":{"slug":"/javascript/garbage-collection/"}}},{"node":{"id":"872218ff-8507-5910-8001-bc52d72fca27","excerpt":"원글: https://javascript.plainenglish.io/6-caching-strategies-to-remember-while-designing-cache-system-da058a3757cf 캐시 시스템 관련 용어, 읽기 vs 쓰기 중심의 애플리케이션에서 캐싱 전략, 캐시를 무효화하는 방법 등에 대해서 알려드리겠습니다. 개요 캐싱의 목표는 원본 소스에서 데이터를 가져오는 횟수를 줄여 처리 속도를 높이고 대기 시간을 감소시키는 것입니다. 캐싱은 인메모리 캐싱, 디스크 캐싱, 데이터베이스 캐싱, CDN 캐싱과 같은 다양한 수준의 아키텍처에서 구현될 수 있습니다. 데이터는 각각 고유한 이점이 있는 다양한 기술을 사용하여 캐싱할 수 있습니다. 인메모리 캐싱은 컴퓨터의 주 메모리에 데이터를 저장하여 디스크 저장소 보다 빠른 액세스를 제공합니다. 반면 디스크 캐싱은 하드 디스크에 데이터를 저장하므로 주 메모리보다는 느리지만 원격 소스에서 데이터를 가져오는 것보다 빠릅니다. 데이터…","fields":{"slug":"/cs/6-caching-strategies/"},"frontmatter":{"categories":"translate cs","title":"(번역) 캐시 시스템 설계할 때 기억해야 할 6가지 캐싱 전략","date":"June 13, 2023","draft":false}},"next":{"fields":{"slug":"/javascript/metaprogramming/"}},"previous":{"fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"}}},{"node":{"id":"09bde05f-002e-5468-8934-6141fab54678","excerpt":"원문: https://blog.openreplay.com/deep-copying-objects-with-the-structuredclone-api/ Deep Copying Objects with the StructuredClone API 개요: 자바스크립트에서 객체를 복사하는 것은 간단하지 않으며, 이는 잘 알려진 문제입니다. 그러나 이 글에서는 해결책을 제공합니다. StructuredClone API를 사용하면 모든 객체를 간단하고 빠르게 복사할 수 있습니다. 자바스크립트에서 객체가 변수에 저장될 때, 해당 변수는 객체의 참조값을 갖습니다. 이는 변수 자체에 객체를 저장하는 것이 아니라, 객체의 메모리 위치를 나타내는 식별자를 저장한다는 것을 의미합니다. 객체의 복사는 원시 타입과는 다른 방식으로 동작합니다. 얕은 복사(Shallow Copy) vs. 깊은 복사(Deep Copy) 자바스크립트에서 값은 두 가지 방법으로 복사할 수 있습니다. 얕은 복사와 깊은 복사입니다. 얕은 복…","fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"},"frontmatter":{"categories":"translate javascript","title":"(번역) StructuredClone API를 사용하여 객체를 깊은 복사하는 법","date":"May 07, 2023","draft":false}},"next":{"fields":{"slug":"/feature/input-date/"}},"previous":{"fields":{"slug":"/javascript/metaprogramming/"}}}]}}, "staticQueryHashes": ["1073350324","1956554647","2938748437"]} \ No newline at end of file diff --git a/page-data/posts/troubleshooting/page-data.json b/page-data/posts/troubleshooting/page-data.json index 2a3626fa..7b2227b6 100644 --- a/page-data/posts/troubleshooting/page-data.json +++ b/page-data/posts/troubleshooting/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-category-template-js", "path": "/posts/troubleshooting", - "result": {"pageContext":{"currentCategory":"troubleshooting","categories":["All","framework","react","next","translate","typescript","browser","a11y","infra","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"e19abd3b-8537-528d-a31e-3a74ebbb5cc5","excerpt":"Introduction #1077 이슈를 보면 mutateFn의 variable을 필수값으로 변경한 이력을 볼 수 있다. mutation인데 왜 variable이 없냐고? 나의 경우에는 URL의 있는 queryParams를 읽어와서 데이터를 넘겨주면 되기 때문에 useSomething hook 안에서 처리하고 싶었다. 문제상황 mutate() 와 같이 variable에 아무 값도 넣지 않고 mutation 함수를 호출 할 수 없다.\nTVariables 타입에 undefined도 줘보고 이래저래 별짓을 다해봐도 필수값을 optional 하게 바꿀수는 없었다. 에러 발생\n 해결 실질적으로는 mute 함수에서 variable을 정의해서 넣어주는 것이기 때문에 그렇게 꼼수해결법도 아니다.","fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"},"frontmatter":{"categories":"react react-query troubleshooting","title":"React Query useMutation에서 variable 옵셔널하게 사용하기","date":"February 15, 2023","draft":false}},"next":{"fields":{"slug":"/react/react-query-basic/"}},"previous":{"fields":{"slug":"/react/recoil-introduction/"}}}]}}, + "result": {"pageContext":{"currentCategory":"troubleshooting","categories":["All","framework","react","next","translate","typescript","browser","infra","a11y","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"e19abd3b-8537-528d-a31e-3a74ebbb5cc5","excerpt":"Introduction #1077 이슈를 보면 mutateFn의 variable을 필수값으로 변경한 이력을 볼 수 있다. mutation인데 왜 variable이 없냐고? 나의 경우에는 URL의 있는 queryParams를 읽어와서 데이터를 넘겨주면 되기 때문에 useSomething hook 안에서 처리하고 싶었다. 문제상황 mutate() 와 같이 variable에 아무 값도 넣지 않고 mutation 함수를 호출 할 수 없다.\nTVariables 타입에 undefined도 줘보고 이래저래 별짓을 다해봐도 필수값을 optional 하게 바꿀수는 없었다. 에러 발생\n 해결 실질적으로는 mute 함수에서 variable을 정의해서 넣어주는 것이기 때문에 그렇게 꼼수해결법도 아니다.","fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"},"frontmatter":{"categories":"react react-query troubleshooting","title":"React Query useMutation에서 variable 옵셔널하게 사용하기","date":"February 15, 2023","draft":false}},"next":{"fields":{"slug":"/react/react-query-basic/"}},"previous":{"fields":{"slug":"/react/recoil-introduction/"}}}]}}, "staticQueryHashes": ["1073350324","1956554647","2938748437"]} \ No newline at end of file diff --git a/page-data/posts/typescript/page-data.json b/page-data/posts/typescript/page-data.json index b5246122..39200ab6 100644 --- a/page-data/posts/typescript/page-data.json +++ b/page-data/posts/typescript/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-category-template-js", "path": "/posts/typescript", - "result": {"pageContext":{"currentCategory":"typescript","categories":["All","framework","react","next","translate","typescript","browser","a11y","infra","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"7af1f27b-4f32-56a8-8140-5191c8ba3150","excerpt":"원글: https://www.omarileon.me/blog/typescript-as-const 새로운 구문인 “상수 어설션(const assertions)“은 TypeScript 3.4에서 도입되었습니다. 이것은 변수가 변경되지 않을 것(immutable)이고 가능한 엄격한 타입을 제공해야 한다는 것을 타입스크립트에 알려줍니다. 이는 타입의 종류에 따라 다르게 영향을 미치므로, 이 글에서는 상수 어설션을 어떻게 사용하고 왜 유용한지에 대해 설명할 것입니다. 문자열/숫자 문자열이나 숫자에 “as const”를 추가하면 특정 값으로 타입을 좁힐 수 있습니다. 숫자의 경우 문자열 또는 숫자의 경우 일반적으로 “const”를 사용하여 변수를 정의하면 동일한 효과를 얻을 수 있기 때문에 덜 유용합니다. 런타임 안전성이라는 추가 이점도 있습니다. 때로는 변수로 값을 정의하지 않고, 단순히 문자열 리터럴을 사용하고 싶을 수 있습니다. 예를 들어 값을 반환할 때처럼요. 그때 “as const”…","fields":{"slug":"/typescript/typescript-as-const/"},"frontmatter":{"categories":"translate typescript","title":"(번역) 타입스크립트에서 'As Const' 이해하기","date":"April 14, 2024","draft":false}},"next":{"fields":{"slug":"/typescript/as-const-vs-satisfies/"}},"previous":{"fields":{"slug":"/react/process-env-destructuring-error.md/"}}},{"node":{"id":"863af574-c232-55ba-952a-d2a2e6490845","excerpt":"Argument of type ‘string’ is not assignable to parameter of type ‘“apple” | “banana”‘.(2345) 타입스크립트 사용시 때때로 예상치 못한 타입 호환성 문제에 직면하곤 합니다. 엄격한 타입 시스템을 가지고 있는 타입스크립트로 코드를 작성하다 보면 더욱 이러한 문제와 자주 직면합니다. 하지만 다행히도, 타입스크립트는 이러한 문제들을 해결할 수 있는 강력한 도구들을 제공합니다. 이 글에서는 타입 호환성 문제를 해결하는 데 있어 as const와 satisfies라는 두 가지 도구의 사용 방법과 각각의 장점에 대해 알아보겠습니다. 먼저 우리가 자주 마주할 수 있는 상황을 예시 코드로 살펴보겠습니다. 위에 에러 메시지는 함수의 인자로 예상되는 타입과 실제로 전달된 타입이 일치하지 않을 때 발생합니다. 이 경우, printPerson 함수는 \"apple\" 또는 \"banana\"라는 구체적인 문자열 타입을 기대하지만, perso…","fields":{"slug":"/typescript/as-const-vs-satisfies/"},"frontmatter":{"categories":"typescript","title":"타입스크립트 타입 호환성 문제 해결하기 \"as const vs satisfies\"","date":"March 31, 2024","draft":false}},"next":{"fields":{"slug":"/react/html-node-streaming/"}},"previous":{"fields":{"slug":"/typescript/typescript-as-const/"}}}]}}, + "result": {"pageContext":{"currentCategory":"typescript","categories":["All","framework","react","next","translate","typescript","browser","infra","a11y","react-query","javascript","cs","feature","troubleshooting","test"],"edges":[{"node":{"id":"7af1f27b-4f32-56a8-8140-5191c8ba3150","excerpt":"원글: https://www.omarileon.me/blog/typescript-as-const 새로운 구문인 “상수 어설션(const assertions)“은 TypeScript 3.4에서 도입되었습니다. 이것은 변수가 변경되지 않을 것(immutable)이고 가능한 엄격한 타입을 제공해야 한다는 것을 타입스크립트에 알려줍니다. 이는 타입의 종류에 따라 다르게 영향을 미치므로, 이 글에서는 상수 어설션을 어떻게 사용하고 왜 유용한지에 대해 설명할 것입니다. 문자열/숫자 문자열이나 숫자에 “as const”를 추가하면 특정 값으로 타입을 좁힐 수 있습니다. 숫자의 경우 문자열 또는 숫자의 경우 일반적으로 “const”를 사용하여 변수를 정의하면 동일한 효과를 얻을 수 있기 때문에 덜 유용합니다. 런타임 안전성이라는 추가 이점도 있습니다. 때로는 변수로 값을 정의하지 않고, 단순히 문자열 리터럴을 사용하고 싶을 수 있습니다. 예를 들어 값을 반환할 때처럼요. 그때 “as const”…","fields":{"slug":"/typescript/typescript-as-const/"},"frontmatter":{"categories":"translate typescript","title":"(번역) 타입스크립트에서 'As Const' 이해하기","date":"April 14, 2024","draft":false}},"next":{"fields":{"slug":"/typescript/as-const-vs-satisfies/"}},"previous":{"fields":{"slug":"/react/process-env-destructuring-error.md/"}}},{"node":{"id":"863af574-c232-55ba-952a-d2a2e6490845","excerpt":"Argument of type ‘string’ is not assignable to parameter of type ‘“apple” | “banana”‘.(2345) 타입스크립트 사용시 때때로 예상치 못한 타입 호환성 문제에 직면하곤 합니다. 엄격한 타입 시스템을 가지고 있는 타입스크립트로 코드를 작성하다 보면 더욱 이러한 문제와 자주 직면합니다. 하지만 다행히도, 타입스크립트는 이러한 문제들을 해결할 수 있는 강력한 도구들을 제공합니다. 이 글에서는 타입 호환성 문제를 해결하는 데 있어 as const와 satisfies라는 두 가지 도구의 사용 방법과 각각의 장점에 대해 알아보겠습니다. 먼저 우리가 자주 마주할 수 있는 상황을 예시 코드로 살펴보겠습니다. 위에 에러 메시지는 함수의 인자로 예상되는 타입과 실제로 전달된 타입이 일치하지 않을 때 발생합니다. 이 경우, printPerson 함수는 \"apple\" 또는 \"banana\"라는 구체적인 문자열 타입을 기대하지만, perso…","fields":{"slug":"/typescript/as-const-vs-satisfies/"},"frontmatter":{"categories":"typescript","title":"타입스크립트 타입 호환성 문제 해결하기 \"as const vs satisfies\"","date":"March 31, 2024","draft":false}},"next":{"fields":{"slug":"/react/html-node-streaming/"}},"previous":{"fields":{"slug":"/typescript/typescript-as-const/"}}}]}}, "staticQueryHashes": ["1073350324","1956554647","2938748437"]} \ No newline at end of file diff --git a/page-data/react/storybook-react-server-components/page-data.json b/page-data/react/storybook-react-server-components/page-data.json index 78559b75..bbf95a39 100644 --- a/page-data/react/storybook-react-server-components/page-data.json +++ b/page-data/react/storybook-react-server-components/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-templates-blog-template-js", "path": "/react/storybook-react-server-components/", - "result": {"data":{"cur":{"id":"5fc01a99-7197-518c-a9de-60ace2976502","html":"
\n

원문: https://storybook.js.org/blog/storybook-react-server-components/

\n
\n
\n

스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용

\n
\n

리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다.

\n

가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다.

\n

이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 생깁니다.

\n

오늘은 이 질문에 대한 실험적인 답변으로 Next.js 프레임워크에서 스토리북의 RSC 지원을 공식적으로 발표하게 되어 기쁩니다. 이것은 완전히 클라이언트 측 구현이므로, 스토리북 애드온과 인테그레이션의 모든 생태계와 호환이 가능합니다.

\n

이것이 어떻게 작동하는지, 사용하는지, 그리고 지금 바로 사용해 볼 수 있는 방법에 대해 알아보고 싶다면 계속 읽어보세요!

\n

화성에서 온 서버, 금성에서 온 클라이언트

\n

RSC는 기존의 클라이언트 컴포넌트와 두 가지 주요 차이점이 있으며, 다음 예제에서 이에 대해 보여주고 있습니다.

\n
// ApiCard.tsx\n\nimport { ComponentProps } from 'react';\nimport { Card } from './Card';\nimport { findById } from './db';\n\nexport async function DbCard({ id }: {id: number}) {\n  let props;\n  try {\n    const contact = await findById(id);\n    props = { state: 'success', contact };\n  } catch (e) {\n    props = { state: 'error' };\n  }\n  return <Card {...props} />;\n}
\n
    \n
  1. 첫 번째 차이점은 컴포넌트가 async 라는 점인데, 이것은 클라이언트에서는 지원되지 않는 기능입니다.
  2. \n
  3. 두 번째 차이점은 컴포넌트가 Node 코드에 직접 액세스할 수 있다는 것인데, 위의 경우에서는 인증된 데이터베이스 연결을 래핑하는 findById 함수에 액세스하고 있습니다.
  4. \n
\n

RSC는 이러한 두 가지 차이점을 구현하기 위해 내부적으로 많은 작업을 수행합니다. 이 코드는 서버에서만 실행되며 클라이언트로 스트리밍되는 정적 JSON과 유사한 구조를 생성합니다.

\n

Storybook은 순수 클라이언트 애플리케이션입니다. Node가 전혀 없는 순수한 HTML/CSS/JS의 정적 빌드를 생성합니다! 따라서 RSC를 지원하려면 RSC를 클라이언트에서 렌더링하거나 Storybook을 서버용으로 다시 설계하는 방법을 찾아야 합니다.

\n

우리는 먼저 클라이언트 접근 방식에 중점을 두어 시작했습니다. 현재 아키텍처를 기반으로 수백 개의 애드온과 수백만 개의 스토리를 작성한 사용자에게 미치는 영향을 최소화하고 싶습니다.

\n

어떻게 클라이언트에서 동작하게 할 수 있었을까요?

\n

비동기로 처리하기

\n

RSC를 클라이언트에서 렌더링하는 데, 첫 번째 과제는 async 컴포넌트를 지원하는 방법을 알아내는 것입니다. 이미 Next.js의 canary 리액트 버전에서 비공식적으로 지원되고 있습니다. 이 간단한 솔루션에 기여한  JamesManningR 및 julRuss에게 특별한 감사를 전합니다!

\n
import { Suspense } from 'react';\n\nexport const ClientContact = ({ id }) => (\n  <Suspense><DbCard id={id} /></Suspense>\n);
\n

Storybook 8에서부터 @storybook/nextjs.storybook/main.js 파일에서 experimentalNextRSC 기능 플래그를 사용하여 스토리를 Suspense로 감싸줄 수 있습니다.

\n
// .storybook/main.js\nexport default {\n  features: {\n    experimentalNextRSC: true,\n  }\n};
\n

7.x 버전의 @storybook/nextjs에서도 RSC 스토리를 데코레이터로 래핑하여 수동으로 이 작업을 수행할 수도 있습니다.

\n

참고: 이 솔루션은 Next.js의 canary 버전 이외의 다른 Storybook React 프레임워크 (예: react-vitereact-webpack5)에서는 아직 작동하지 않습니다. 다음 버전에서는 이러한 제한이 해제되기를 바랍니다.

\n

모킹 및 로딩

\n

비동기 문제를 해결하는 것은 절반의 성공에 불과합니다. DbCard 컴포넌트는 데이터를 가져와 컴포넌트를 채우는 Node 코드를 참조합니다. 이것은 브라우저에서 Node 코드를 실행할 수 없는 문제가 있습니다!

\n

차선책으로 이 문제를 해결하기 위해서는, 깔끔한 데이터 액세스 계층 구축이 필요합니다. 이것은 또한 RSC의 설계자가 추천하는 최상의 권장 방법입니다.

\n

데이터 액세스 계층이 있으면, 브라우저에서 실행할 수 있도록 모킹할 수 있고 데이터를 정확하게 제어하여 다양한 UI 상태(로딩, 오류, 성공, 등)를 테스트할 수 있습니다.

\n

데이터 액세스 계층을 모킹하기 위해서는 Storybook에서 지원하는 모듈 모킹 또는 네트워크 모킹를 사용할 수 있습니다.

\n

모듈: jest.mock 스타일의 목을 제공하는 storybook-addon-module-mock 커뮤니티 애드온이 있습니다(웹팩 프로젝트에서만 지원). 더 간단하지만 더 제한적인 해결책으로 webpack/vite 별칭을 사용할 수도 있습니다. 향후 버전의 Storybook에서 모듈 모킹을 더 효율적으로 제공할 계획입니다.

\n

네트워크 API: 네트워크 요청을 모킹하기 위해 Mock Service Worker (msw)를 추천합니다. Storybook은 다양한 네트워크GraphQL 모킹 애드온을 지원합니다.

\n

우리의 예제로 돌아와서, storybook-addon-module-mock을 사용한 스토리는 다음과 같습니다.

\n
// DbCard.stories.js\n\nimport { StoryObj, Meta } from '@storybook/react';\nimport { createMock } from 'storybook-addon-module-mock';\n\nimport { DbCard } from './DbCard';\nimport * as db from './db';\n\nexport default { component: DbCard };\n\nexport const Success {\n  args: { id: 1 },\n  parameters: {\n    moduleMock: {\n      mock: () => {\n        const mock = createMock(db, 'findById');\n        mock.mockReturnValue(Promise.resolve({\n          name: 'Beyonce',\n          img: 'https://blackhistorywall.files.wordpress.com/2010/02/picture-device-independent-bitmap-119.jpg',\n          tel: '+123 456 789',\n          email: 'b@beyonce.com'\n        }))\n        return [mock];\n      },\n    },\n  },\n}
\n

API + 모듈 모킹의 전체 데모

\n

데이터베이스의 모듈 모킹 버전과 MSW2로 모킹된 API 버전을 포함한 위의 전체 예제에 대한 전체 내용은 우리의 전체 RSC 데모 Storybook 또는 GitHub 레포지토리를 확인하십시오.

\n

\"\"

\n

주의사항

\n

이 글에서는, Storybook에서 첫 번째 RSC에 대한 스토리를 성공적으로 작성했으며 이 모든 것이 내부적으로 어떻게 구현되었는지 보여주었습니다.

\n

이 모든 것은 꽤 간단한 과정이었지만, 이 접근 방식에는 제한 사항이 있습니다.

\n
    \n
  1. 일치성. 순수한 클라이언트 구현은 애플리케이션에서 실행되는 서버측 스트리밍 RSC 구현과 크게 다릅니다.
  2. \n
  3. 편의성. 여기서 제공하는 모킹 솔루션은 개선될 여지가 있습니다. 현재의 모듈 모킹 솔루션은 장황할 뿐만 아니라 Storybook의 args/controls와 잘 호환되지 않습니다.
  4. \n
\n

우리는 이러한 제한 사항을 향후에 해결할 계획이므로, 이 솔루션을 실험 단계로 표시했습니다.

\n

오늘부터 RSC를 위한 Storybook 사용하기 🎊

\n

RSC를 위한 Storybook을 사용하려면 Storybook을 8.0-alpha로 업그레이드하십시오.

\n
npx storybook@next upgrade --prerelease\n
\n

그런 다음 .storybook/main.ts에서 실험적 기능을 활성화하세요.

\n
// .storybook/main.js\nexport default {\n  features: {\n    experimentalNextRSC: true,\n  },\n};\n
\n

더 많은 정보를 원하시면, @storybook/nextjs README를 참조하세요.

\n

이것은 다음 메이져 버전인 Storybook 8.0의 내용을 자세히 설명하는 첫 번째 게시물이며, 앞으로 몇 달 안에 더 많은 내용이 추가될 예정입니다. 다음 릴리스에 대한 모든 소식을 확인하려면 소셜 미디어에서 팔로우하거나 Storybook 뉴스레터에 가입하세요!

\n","excerpt":"원문: https://storybook.js.org/blog/storybook-react-server-components/ 스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용 리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다. 가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다. 이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 …","frontmatter":{"date":"February 02, 2024","title":"(번역) 리액트 서버 컴포넌트를 위한 스토리북","categories":"translate react","author":"soobing"},"fields":{"slug":"/react/storybook-react-server-components/"}},"next":{"id":"161feee4-18f2-5161-8900-a9bc5eda4367","html":"

지난 글에서 react-query의 hydrate, dehydrate을 통해 서버에서 prefetching 한 데이터 사용하는 방법에 대해서 살펴보았습니다.

\n
\n

서버에서 prefetching 한 데이터 사용하기

\n
\n

오늘은 조금 실용적으로 Next.js 13, 14 버전의 app router에서 react-query를 어떻게 사용하고 세팅하면 좋을지 고민했던 내용에 대해서 이야기해보도록 하겠습니다.

\n
\n

해당 글은 23년 10월에 메이저 버전 업데이트된, Next.js 14React-Query 5 를 기준으로 작성된 글입니다.

\n
\n
\n

예시로 보여주는 코드는 저의 next-14-react-query repo에서 확인 할 수 있습니다.

\n
\n

제가 크게 고민했던 문제는 아래 세 가지입니다.

\n\n

jsonplaceholder API 서버의 photo 서비스를 사용하는 것을 예시로 프로젝트 구조를 어떻게 설계했는지, 그리고 위에서 했던 고민들에 대한 이야기를 나눠 보도록 하겠습니다.

\n

초기 환경 세팅

\n
/** hooks/useReactQuery.tsx */\n\n'use client'\n\nimport { useState } from 'react'\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query'\nimport { ReactQueryDevtools } from '@tanstack/react-query-devtools';\n\nexport default function ReactQueryProviders({ children }: React.PropsWithChildren) {\n  const [queryClient] = useState(\n    () =>\n      new QueryClient({\n        defaultOptions: {\n          queries: {\n            // With SSR, we usually want to set some default staleTime\n            // above 0 to avoid refetching immediately on the client\n            staleTime: 60 * 1000,\n          },\n        },\n      }),\n  )\n\n  return (\n    <QueryClientProvider client={queryClient}>\n      {children}\n      <ReactQueryDevtools initialIsOpen={false} />\n    </QueryClientProvider>\n  )\n}
\n
/** app/layout.tsx */\n\nimport ReactQueryProviders from '@/hooks/useReactQuery'\n\nexport default function RootLayout({ children }: React.PropsWithChildren) {\n  return (\n    <html lang="en">\n      <head />\n      <body>\n        <ReactQueryProviders>{children}</ReactQueryProviders>\n      </body>\n    </html>\n  )\n}
\n

클라이언트, 서버 모두에서 사용될 수 있도록 서비스 설계하기

\n

jsonplaceholder API 페이지를 가보면, 서비스 별로 다음과 같은 라우터 구조를 가지고 있다고 명세되어 있습니다.

\n

\n \n \n

\n

저는 photo 서비스를 기준으로 아래와 같이 폴더를 생성해 보았습니다.

\n

\n \n \n

\n

API의 서비스별로 따로 관리하면 가독성과 유지 보수에 좋습니다. 그래서 PhotoService를 작성하면 아래와 같습니다.

\n
/** service/photo/PhotoService.ts */\n\nimport Service from '@/service/Service';\nimport {Photo} from '@/model/photo';\n\nclass PhotoService extends Service {\n  getPhotos() {\n    return this.http.get<Photo[]>(\n      `/photos`,\n    );\n  }\n\n  getPhoto(photoId: number) {\n    return this.http.get<Photo>(\n      `/photo/${photoId}`,\n    );\n  }\n\n  getComments(photoId: number) {\n    return this.http.get<Comment[]>(\n      `/photo/${photoId}/comments`,\n    );\n  }\n\n  getComment({photoId, commentId}: {photoId: number, commentId: number}) {\n    return this.http.get<Comment[]>(\n      `/photo/${photoId}/comments/${commentId}`,\n    );\n  }\n}\n\nexport default new PhotoService();
\n

그리고 react-query의 useQuery를 사용 할 때, 필요한 곳에서 직접적으로 호출해서 사용하기보다는 이 또한 서비스별로 Colocate 시켜서 가독성과 유지 보수하기 좋도록 설계했습니다.

\n
\n

관련해서 유명한 글인 Maintainability through colocation by Kent C. Dodds를 읽어보는 것을 추천합니다.

\n
\n
/** service/photo/usePhotoService.ts */\n\nimport { useQuery } from '@tanstack/react-query';\nimport queryOptions from '@/service/photo/queries';\n\nexport function usePhotos() {\n  return useQuery(queryOptions.all());\n}\n\nexport function usePhoto({photoId}: {photoId: number}) {\n  return useQuery(queryOptions.detail(photoId));\n}\n\nexport function useComments({photoId}: {photoId: number}) {\n  return useQuery(queryOptions.comments(photoId));\n}\n\nexport function useComment({photoId, commentId}: {photoId: number, commentId: number}) {\n  return useQuery(queryOptions.comment({photoId, commentId}));\n}
\n

여기서 queries라는 파일에서 queryOption을 빼오고 있는데, 해당 파일에서는 QueryKey와 QueryOption을 모두 관리하고 있습니다. 이것은 react-query의 메인테이너인 TkDodo’s blog에서 효율적으로 React Query Key 관리하는 법 이라는 글을 참고하여 작성했습니다.

\n
import PhotoService from '@/service/photo/PhotoService';\n\nconst queryKeys = {\n  all: ['photos'] as const,\n  detail: (photoId: number) => [...queryKeys.all, photoId] as const,\n  detailComments: (photoId: number) => [...queryKeys.detail(photoId), 'comments'] as const,\n  detailComment: ({photoId, commentId}: {photoId: number, commentId: number}) => [...queryKeys.detailComments(photoId), commentId] as const,\n};\n\nconst queryOptions = {\n  all: () => ({\n    queryKey: queryKeys.all,\n    queryFn: () => PhotoService.getPhotos(),\n  }),\n  detail: (photoId: number) => ({\n    queryKey: queryKeys.detail(photoId),\n    queryFn: () => PhotoService.getPhoto(photoId),\n  }),\n  comments: (photoId: number) => ({\n    queryKey: queryKeys.detailComments(photoId),\n    queryFn: () => PhotoService.getComments(photoId),\n  }),\n  comment: ({photoId, commentId}: {photoId: number, commentId: number}) => ({\n    queryKey: queryKeys.detailComment({photoId, commentId}),\n    queryFn: () => PhotoService.getComment({photoId, commentId}),\n  }),\n};\n\nexport default queryOptions;
\n

위의 글에서는 queryKey 관리에 대한 이야기만 나와있지만, SSR이나 RSC에서 Hydrate API를 사용하는 경우가 많아져서, queryKey와 비슷하게 queryOption도 함께 관리하도록 추가했습니다.

\n

서버에서 Prefetching 하고 데이터 de/hydration 하기

\n

hydration에 대한 개념적인 설명은 지난 글을 참고해 주시면 좋을것 같습니다. 서버에서 prefetch 한 쿼리들을 dehydrate 시켰다가 클라이언트에서 hydration 시켜줘야 합니다. 이렇게 해주면 서버에서 prefetch 하여 react-query로 캐싱 한 쿼리들을 클라이언트에서 사용할 때도 유지되어서 refetch 하지 않습니다.

\n
import {\n  HydrationBoundary,\n  QueryClient,\n  dehydrate,\n  QueryState,\n  QueryKey\n} from '@tanstack/react-query';\nimport { cache } from 'react';\nimport { isEqual } from '@/utils';\n\nexport const getQueryClient = cache(() => new QueryClient());\n\ntype UnwrapPromise<T> = T extends Promise<infer U> ? U : T;\n\ninterface QueryProps<ResponseType = unknown> {\n  queryKey: QueryKey;\n  queryFn: () => Promise<ResponseType>;\n}\n\ninterface DehydratedQueryExtended<TData = unknown, TError = unknown> {\n  state: QueryState<TData, TError>;\n}\n\nexport async function getDehydratedQuery<Q extends QueryProps>({\n  queryKey,\n  queryFn,\n}: Q) {\n  const queryClient = getQueryClient();\n  await queryClient.prefetchQuery({ queryKey, queryFn });\n\n  const { queries } = dehydrate(queryClient);\n  const [dehydratedQuery] = queries.filter((query) =>\n    isEqual(query.queryKey, queryKey),\n  );\n\n  return dehydratedQuery as DehydratedQueryExtended<\n    UnwrapPromise<ReturnType<Q['queryFn']>>\n  >;\n}\n\nexport const Hydrate = HydrationBoundary;\n\nexport default {};
\n

위의 코드에서는 getQueryClientgetDehydratedQuery 두 가지 함수를 유틸로 export 하고 있습니다. getQueryClient 유틸은 공식 문서에서 권장 하고 있듯이, 서버에서 데이터를 fetching 할 때 마다 필요한 queryClient를 cache 해서 사용할 수 있도록 했습니다.

\n

getDehydratedQuery는 queryClient를 이용하여 서버에서 데이터를 prefetching 하고 dehydrate 한 결과물을 리턴하는 코드입니다.

\n

위 유틸을 사용하여 SSR을 수행하고 있는 page.tsx 파일에서 서버에서 prefeching 한 데이터를 hydration 시켰을 때 아래와 같이 inactive 상태로 보이는 것을 볼 수 있습니다.

\n
import Image from 'next/image'\nimport styles from './page.module.css'\nimport PhotoList from '@/components/PhotoList'\nimport queryOptions from '@/service/photo/queries';\nimport { Hydrate, getDehydratedQuery } from '@/utils/react-query';\n\nexport default async function Home() {\n  const { queryKey, queryFn } = queryOptions.all();\n\n  const query = await getDehydratedQuery({ queryKey, queryFn });\n\n  return (\n    <main className={styles.main}>\n     ...\n\n    {/* 서버 사이드 렌더링 & 서버 컴포넌트 */}\n    <Hydrate state={{ queries: [query] }}>\n      {/* Client Component 잠시 주석처리 */}\n      {/* <PhotoList/> */}\n    </Hydrate>\n    </main>\n  )\n}
\n

\n \n \n

\n

덜 복잡한 코드를 작성할 순 없을까? (ReactQueryStreamedHydration)

\n

끝으로, 이렇게 dehydrate, hydrate 직접 시켜줘야 하는 코드가 심플하지 못하다고 생각하여 더 좋은 방법이 없는가 검색하던 중에 ReactQueryStreamedHydration을 발견했습니다. prefetching 없이도 streaming SSR을 작동 시키는 방법인데, 이 패키지를 사용하면 초기 요청 동안 서버에서 데이터를 가져오고 (useQuery 훅에서의 API 호출이 서버에서 이루어짐) 데이터가 준비되면 QueryClient에 전달하여 root에서 hydration을 수행하기 때문에 컴포넌트에서 useSuspenseQuery를 호출하기만 하면 됩니다. CSR에서 react-query를 사용하듯이 코드가 엄청나게 간단해집니다. 이는 놀라운 개발 경험(DX)과 낮은 코드 복잡성을 제공합니다.

\n

그러나 이 방법을 선택하지 않은 이유는, 실험 단계 인점이 가장 컸고 깊게 중첩된 쿼리가 없고 요청 최적화를 잘 관리하고 있는 경우 사용하기를 권장하고 있는데 따로 잘 관리할 자신이 없었던 점이 제일 컸습니다. 조금 더 자세히 알아보고 싶으신 분들은 공식 문서와 라이브러리 코드를 참고하시면 좋을 것 같습니다 :)

\n

참고자료

\n\n","frontmatter":{"date":"January 07, 2024","title":"Next.js app router에서 React Query 사용하면서 고민했던 것들","categories":"react react-query","author":"soobing"},"fields":{"slug":"/react/next-app-router-react-query/"}},"prev":{"id":"4cbf47cf-4a24-5ffd-88c3-bdede11e8a72","html":"

Docker 엔진 시작하기/종료하기

\n\n

컨테이너의 기본적인 사용 방법

\n\n

컨테이너 생성, 삭제, 실행, 정지

\n
docker run (옵션) 이미지 (인자)\ndocker stop 컨테이너_이름\ndocker rm 컨테이너_이름\ndocker ps -a
\n

예제

\n\n","frontmatter":{"date":"February 18, 2024","title":"docker에서 자주쓰는 명령어","categories":"infra","author":"soobing"},"fields":{"slug":"/infra/docker-command/"}},"site":{"siteMetadata":{"siteUrl":"https://soobing.github.io","comments":{"utterances":{"repo":"soobing/soobing.github.io"}}}}},"pageContext":{"slug":"/react/storybook-react-server-components/","nextSlug":"/react/next-app-router-react-query/","prevSlug":"/infra/docker-command/"}}, + "result": {"data":{"cur":{"id":"5fc01a99-7197-518c-a9de-60ace2976502","html":"
\n

원문: https://storybook.js.org/blog/storybook-react-server-components/

\n
\n
\n

스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용

\n
\n

리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다.

\n

가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다.

\n

이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 생깁니다.

\n

오늘은 이 질문에 대한 실험적인 답변으로 Next.js 프레임워크에서 스토리북의 RSC 지원을 공식적으로 발표하게 되어 기쁩니다. 이것은 완전히 클라이언트 측 구현이므로, 스토리북 애드온과 인테그레이션의 모든 생태계와 호환이 가능합니다.

\n

이것이 어떻게 작동하는지, 사용하는지, 그리고 지금 바로 사용해 볼 수 있는 방법에 대해 알아보고 싶다면 계속 읽어보세요!

\n

화성에서 온 서버, 금성에서 온 클라이언트

\n

RSC는 기존의 클라이언트 컴포넌트와 두 가지 주요 차이점이 있으며, 다음 예제에서 이에 대해 보여주고 있습니다.

\n
// ApiCard.tsx\n\nimport { ComponentProps } from 'react';\nimport { Card } from './Card';\nimport { findById } from './db';\n\nexport async function DbCard({ id }: {id: number}) {\n  let props;\n  try {\n    const contact = await findById(id);\n    props = { state: 'success', contact };\n  } catch (e) {\n    props = { state: 'error' };\n  }\n  return <Card {...props} />;\n}
\n
    \n
  1. 첫 번째 차이점은 컴포넌트가 async 라는 점인데, 이것은 클라이언트에서는 지원되지 않는 기능입니다.
  2. \n
  3. 두 번째 차이점은 컴포넌트가 Node 코드에 직접 액세스할 수 있다는 것인데, 위의 경우에서는 인증된 데이터베이스 연결을 래핑하는 findById 함수에 액세스하고 있습니다.
  4. \n
\n

RSC는 이러한 두 가지 차이점을 구현하기 위해 내부적으로 많은 작업을 수행합니다. 이 코드는 서버에서만 실행되며 클라이언트로 스트리밍되는 정적 JSON과 유사한 구조를 생성합니다.

\n

Storybook은 순수 클라이언트 애플리케이션입니다. Node가 전혀 없는 순수한 HTML/CSS/JS의 정적 빌드를 생성합니다! 따라서 RSC를 지원하려면 RSC를 클라이언트에서 렌더링하거나 Storybook을 서버용으로 다시 설계하는 방법을 찾아야 합니다.

\n

우리는 먼저 클라이언트 접근 방식에 중점을 두어 시작했습니다. 현재 아키텍처를 기반으로 수백 개의 애드온과 수백만 개의 스토리를 작성한 사용자에게 미치는 영향을 최소화하고 싶습니다.

\n

어떻게 클라이언트에서 동작하게 할 수 있었을까요?

\n

비동기로 처리하기

\n

RSC를 클라이언트에서 렌더링하는 데, 첫 번째 과제는 async 컴포넌트를 지원하는 방법을 알아내는 것입니다. 이미 Next.js의 canary 리액트 버전에서 비공식적으로 지원되고 있습니다. 이 간단한 솔루션에 기여한  JamesManningR 및 julRuss에게 특별한 감사를 전합니다!

\n
import { Suspense } from 'react';\n\nexport const ClientContact = ({ id }) => (\n  <Suspense><DbCard id={id} /></Suspense>\n);
\n

Storybook 8에서부터 @storybook/nextjs.storybook/main.js 파일에서 experimentalNextRSC 기능 플래그를 사용하여 스토리를 Suspense로 감싸줄 수 있습니다.

\n
// .storybook/main.js\nexport default {\n  features: {\n    experimentalNextRSC: true,\n  }\n};
\n

7.x 버전의 @storybook/nextjs에서도 RSC 스토리를 데코레이터로 래핑하여 수동으로 이 작업을 수행할 수도 있습니다.

\n

참고: 이 솔루션은 Next.js의 canary 버전 이외의 다른 Storybook React 프레임워크 (예: react-vitereact-webpack5)에서는 아직 작동하지 않습니다. 다음 버전에서는 이러한 제한이 해제되기를 바랍니다.

\n

모킹 및 로딩

\n

비동기 문제를 해결하는 것은 절반의 성공에 불과합니다. DbCard 컴포넌트는 데이터를 가져와 컴포넌트를 채우는 Node 코드를 참조합니다. 이것은 브라우저에서 Node 코드를 실행할 수 없는 문제가 있습니다!

\n

차선책으로 이 문제를 해결하기 위해서는, 깔끔한 데이터 액세스 계층 구축이 필요합니다. 이것은 또한 RSC의 설계자가 추천하는 최상의 권장 방법입니다.

\n

데이터 액세스 계층이 있으면, 브라우저에서 실행할 수 있도록 모킹할 수 있고 데이터를 정확하게 제어하여 다양한 UI 상태(로딩, 오류, 성공, 등)를 테스트할 수 있습니다.

\n

데이터 액세스 계층을 모킹하기 위해서는 Storybook에서 지원하는 모듈 모킹 또는 네트워크 모킹를 사용할 수 있습니다.

\n

모듈: jest.mock 스타일의 목을 제공하는 storybook-addon-module-mock 커뮤니티 애드온이 있습니다(웹팩 프로젝트에서만 지원). 더 간단하지만 더 제한적인 해결책으로 webpack/vite 별칭을 사용할 수도 있습니다. 향후 버전의 Storybook에서 모듈 모킹을 더 효율적으로 제공할 계획입니다.

\n

네트워크 API: 네트워크 요청을 모킹하기 위해 Mock Service Worker (msw)를 추천합니다. Storybook은 다양한 네트워크GraphQL 모킹 애드온을 지원합니다.

\n

우리의 예제로 돌아와서, storybook-addon-module-mock을 사용한 스토리는 다음과 같습니다.

\n
// DbCard.stories.js\n\nimport { StoryObj, Meta } from '@storybook/react';\nimport { createMock } from 'storybook-addon-module-mock';\n\nimport { DbCard } from './DbCard';\nimport * as db from './db';\n\nexport default { component: DbCard };\n\nexport const Success {\n  args: { id: 1 },\n  parameters: {\n    moduleMock: {\n      mock: () => {\n        const mock = createMock(db, 'findById');\n        mock.mockReturnValue(Promise.resolve({\n          name: 'Beyonce',\n          img: 'https://blackhistorywall.files.wordpress.com/2010/02/picture-device-independent-bitmap-119.jpg',\n          tel: '+123 456 789',\n          email: 'b@beyonce.com'\n        }))\n        return [mock];\n      },\n    },\n  },\n}
\n

API + 모듈 모킹의 전체 데모

\n

데이터베이스의 모듈 모킹 버전과 MSW2로 모킹된 API 버전을 포함한 위의 전체 예제에 대한 전체 내용은 우리의 전체 RSC 데모 Storybook 또는 GitHub 레포지토리를 확인하십시오.

\n

\"\"

\n

주의사항

\n

이 글에서는, Storybook에서 첫 번째 RSC에 대한 스토리를 성공적으로 작성했으며 이 모든 것이 내부적으로 어떻게 구현되었는지 보여주었습니다.

\n

이 모든 것은 꽤 간단한 과정이었지만, 이 접근 방식에는 제한 사항이 있습니다.

\n
    \n
  1. 일치성. 순수한 클라이언트 구현은 애플리케이션에서 실행되는 서버측 스트리밍 RSC 구현과 크게 다릅니다.
  2. \n
  3. 편의성. 여기서 제공하는 모킹 솔루션은 개선될 여지가 있습니다. 현재의 모듈 모킹 솔루션은 장황할 뿐만 아니라 Storybook의 args/controls와 잘 호환되지 않습니다.
  4. \n
\n

우리는 이러한 제한 사항을 향후에 해결할 계획이므로, 이 솔루션을 실험 단계로 표시했습니다.

\n

오늘부터 RSC를 위한 Storybook 사용하기 🎊

\n

RSC를 위한 Storybook을 사용하려면 Storybook을 8.0-alpha로 업그레이드하십시오.

\n
npx storybook@next upgrade --prerelease\n
\n

그런 다음 .storybook/main.ts에서 실험적 기능을 활성화하세요.

\n
// .storybook/main.js\nexport default {\n  features: {\n    experimentalNextRSC: true,\n  },\n};\n
\n

더 많은 정보를 원하시면, @storybook/nextjs README를 참조하세요.

\n

이것은 다음 메이져 버전인 Storybook 8.0의 내용을 자세히 설명하는 첫 번째 게시물이며, 앞으로 몇 달 안에 더 많은 내용이 추가될 예정입니다. 다음 릴리스에 대한 모든 소식을 확인하려면 소셜 미디어에서 팔로우하거나 Storybook 뉴스레터에 가입하세요!

\n","excerpt":"원문: https://storybook.js.org/blog/storybook-react-server-components/ 스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용 리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다. 가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다. 이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 …","frontmatter":{"date":"February 02, 2024","title":"(번역) 리액트 서버 컴포넌트를 위한 스토리북","categories":"translate react","author":"soobing"},"fields":{"slug":"/react/storybook-react-server-components/"}},"next":{"id":"161feee4-18f2-5161-8900-a9bc5eda4367","html":"

지난 글에서 react-query의 hydrate, dehydrate을 통해 서버에서 prefetching 한 데이터 사용하는 방법에 대해서 살펴보았습니다.

\n
\n

서버에서 prefetching 한 데이터 사용하기

\n
\n

오늘은 조금 실용적으로 Next.js 13, 14 버전의 app router에서 react-query를 어떻게 사용하고 세팅하면 좋을지 고민했던 내용에 대해서 이야기해보도록 하겠습니다.

\n
\n

해당 글은 23년 10월에 메이저 버전 업데이트된, Next.js 14React-Query 5 를 기준으로 작성된 글입니다.

\n
\n
\n

예시로 보여주는 코드는 저의 next-14-react-query repo에서 확인 할 수 있습니다.

\n
\n

제가 크게 고민했던 문제는 아래 세 가지입니다.

\n\n

jsonplaceholder API 서버의 photo 서비스를 사용하는 것을 예시로 프로젝트 구조를 어떻게 설계했는지, 그리고 위에서 했던 고민들에 대한 이야기를 나눠 보도록 하겠습니다.

\n

초기 환경 세팅

\n
/** hooks/useReactQuery.tsx */\n\n'use client'\n\nimport { useState } from 'react'\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query'\nimport { ReactQueryDevtools } from '@tanstack/react-query-devtools';\n\nexport default function ReactQueryProviders({ children }: React.PropsWithChildren) {\n  const [queryClient] = useState(\n    () =>\n      new QueryClient({\n        defaultOptions: {\n          queries: {\n            // With SSR, we usually want to set some default staleTime\n            // above 0 to avoid refetching immediately on the client\n            staleTime: 60 * 1000,\n          },\n        },\n      }),\n  )\n\n  return (\n    <QueryClientProvider client={queryClient}>\n      {children}\n      <ReactQueryDevtools initialIsOpen={false} />\n    </QueryClientProvider>\n  )\n}
\n
/** app/layout.tsx */\n\nimport ReactQueryProviders from '@/hooks/useReactQuery'\n\nexport default function RootLayout({ children }: React.PropsWithChildren) {\n  return (\n    <html lang="en">\n      <head />\n      <body>\n        <ReactQueryProviders>{children}</ReactQueryProviders>\n      </body>\n    </html>\n  )\n}
\n

클라이언트, 서버 모두에서 사용될 수 있도록 서비스 설계하기

\n

jsonplaceholder API 페이지를 가보면, 서비스 별로 다음과 같은 라우터 구조를 가지고 있다고 명세되어 있습니다.

\n

\n \n \n

\n

저는 photo 서비스를 기준으로 아래와 같이 폴더를 생성해 보았습니다.

\n

\n \n \n

\n

API의 서비스별로 따로 관리하면 가독성과 유지 보수에 좋습니다. 그래서 PhotoService를 작성하면 아래와 같습니다.

\n
/** service/photo/PhotoService.ts */\n\nimport Service from '@/service/Service';\nimport {Photo} from '@/model/photo';\n\nclass PhotoService extends Service {\n  getPhotos() {\n    return this.http.get<Photo[]>(\n      `/photos`,\n    );\n  }\n\n  getPhoto(photoId: number) {\n    return this.http.get<Photo>(\n      `/photo/${photoId}`,\n    );\n  }\n\n  getComments(photoId: number) {\n    return this.http.get<Comment[]>(\n      `/photo/${photoId}/comments`,\n    );\n  }\n\n  getComment({photoId, commentId}: {photoId: number, commentId: number}) {\n    return this.http.get<Comment[]>(\n      `/photo/${photoId}/comments/${commentId}`,\n    );\n  }\n}\n\nexport default new PhotoService();
\n

그리고 react-query의 useQuery를 사용 할 때, 필요한 곳에서 직접적으로 호출해서 사용하기보다는 이 또한 서비스별로 Colocate 시켜서 가독성과 유지 보수하기 좋도록 설계했습니다.

\n
\n

관련해서 유명한 글인 Maintainability through colocation by Kent C. Dodds를 읽어보는 것을 추천합니다.

\n
\n
/** service/photo/usePhotoService.ts */\n\nimport { useQuery } from '@tanstack/react-query';\nimport queryOptions from '@/service/photo/queries';\n\nexport function usePhotos() {\n  return useQuery(queryOptions.all());\n}\n\nexport function usePhoto({photoId}: {photoId: number}) {\n  return useQuery(queryOptions.detail(photoId));\n}\n\nexport function useComments({photoId}: {photoId: number}) {\n  return useQuery(queryOptions.comments(photoId));\n}\n\nexport function useComment({photoId, commentId}: {photoId: number, commentId: number}) {\n  return useQuery(queryOptions.comment({photoId, commentId}));\n}
\n

여기서 queries라는 파일에서 queryOption을 빼오고 있는데, 해당 파일에서는 QueryKey와 QueryOption을 모두 관리하고 있습니다. 이것은 react-query의 메인테이너인 TkDodo’s blog에서 효율적으로 React Query Key 관리하는 법 이라는 글을 참고하여 작성했습니다.

\n
import PhotoService from '@/service/photo/PhotoService';\n\nconst queryKeys = {\n  all: ['photos'] as const,\n  detail: (photoId: number) => [...queryKeys.all, photoId] as const,\n  detailComments: (photoId: number) => [...queryKeys.detail(photoId), 'comments'] as const,\n  detailComment: ({photoId, commentId}: {photoId: number, commentId: number}) => [...queryKeys.detailComments(photoId), commentId] as const,\n};\n\nconst queryOptions = {\n  all: () => ({\n    queryKey: queryKeys.all,\n    queryFn: () => PhotoService.getPhotos(),\n  }),\n  detail: (photoId: number) => ({\n    queryKey: queryKeys.detail(photoId),\n    queryFn: () => PhotoService.getPhoto(photoId),\n  }),\n  comments: (photoId: number) => ({\n    queryKey: queryKeys.detailComments(photoId),\n    queryFn: () => PhotoService.getComments(photoId),\n  }),\n  comment: ({photoId, commentId}: {photoId: number, commentId: number}) => ({\n    queryKey: queryKeys.detailComment({photoId, commentId}),\n    queryFn: () => PhotoService.getComment({photoId, commentId}),\n  }),\n};\n\nexport default queryOptions;
\n

위의 글에서는 queryKey 관리에 대한 이야기만 나와있지만, SSR이나 RSC에서 Hydrate API를 사용하는 경우가 많아져서, queryKey와 비슷하게 queryOption도 함께 관리하도록 추가했습니다.

\n

서버에서 Prefetching 하고 데이터 de/hydration 하기

\n

hydration에 대한 개념적인 설명은 지난 글을 참고해 주시면 좋을것 같습니다. 서버에서 prefetch 한 쿼리들을 dehydrate 시켰다가 클라이언트에서 hydration 시켜줘야 합니다. 이렇게 해주면 서버에서 prefetch 하여 react-query로 캐싱 한 쿼리들을 클라이언트에서 사용할 때도 유지되어서 refetch 하지 않습니다.

\n
import {\n  HydrationBoundary,\n  QueryClient,\n  dehydrate,\n  QueryState,\n  QueryKey\n} from '@tanstack/react-query';\nimport { cache } from 'react';\nimport { isEqual } from '@/utils';\n\nexport const getQueryClient = cache(() => new QueryClient());\n\ntype UnwrapPromise<T> = T extends Promise<infer U> ? U : T;\n\ninterface QueryProps<ResponseType = unknown> {\n  queryKey: QueryKey;\n  queryFn: () => Promise<ResponseType>;\n}\n\ninterface DehydratedQueryExtended<TData = unknown, TError = unknown> {\n  state: QueryState<TData, TError>;\n}\n\nexport async function getDehydratedQuery<Q extends QueryProps>({\n  queryKey,\n  queryFn,\n}: Q) {\n  const queryClient = getQueryClient();\n  await queryClient.prefetchQuery({ queryKey, queryFn });\n\n  const { queries } = dehydrate(queryClient);\n  const [dehydratedQuery] = queries.filter((query) =>\n    isEqual(query.queryKey, queryKey),\n  );\n\n  return dehydratedQuery as DehydratedQueryExtended<\n    UnwrapPromise<ReturnType<Q['queryFn']>>\n  >;\n}\n\nexport const Hydrate = HydrationBoundary;\n\nexport default {};
\n

위의 코드에서는 getQueryClientgetDehydratedQuery 두 가지 함수를 유틸로 export 하고 있습니다. getQueryClient 유틸은 공식 문서에서 권장 하고 있듯이, 서버에서 데이터를 fetching 할 때 마다 필요한 queryClient를 cache 해서 사용할 수 있도록 했습니다.

\n

getDehydratedQuery는 queryClient를 이용하여 서버에서 데이터를 prefetching 하고 dehydrate 한 결과물을 리턴하는 코드입니다.

\n

위 유틸을 사용하여 SSR을 수행하고 있는 page.tsx 파일에서 서버에서 prefeching 한 데이터를 hydration 시켰을 때 아래와 같이 inactive 상태로 보이는 것을 볼 수 있습니다.

\n
import Image from 'next/image'\nimport styles from './page.module.css'\nimport PhotoList from '@/components/PhotoList'\nimport queryOptions from '@/service/photo/queries';\nimport { Hydrate, getDehydratedQuery } from '@/utils/react-query';\n\nexport default async function Home() {\n  const { queryKey, queryFn } = queryOptions.all();\n\n  const query = await getDehydratedQuery({ queryKey, queryFn });\n\n  return (\n    <main className={styles.main}>\n     ...\n\n    {/* 서버 사이드 렌더링 & 서버 컴포넌트 */}\n    <Hydrate state={{ queries: [query] }}>\n      {/* Client Component 잠시 주석처리 */}\n      {/* <PhotoList/> */}\n    </Hydrate>\n    </main>\n  )\n}
\n

\n \n \n

\n

덜 복잡한 코드를 작성할 순 없을까? (ReactQueryStreamedHydration)

\n

끝으로, 이렇게 dehydrate, hydrate 직접 시켜줘야 하는 코드가 심플하지 못하다고 생각하여 더 좋은 방법이 없는가 검색하던 중에 ReactQueryStreamedHydration을 발견했습니다. prefetching 없이도 streaming SSR을 작동 시키는 방법인데, 이 패키지를 사용하면 초기 요청 동안 서버에서 데이터를 가져오고 (useQuery 훅에서의 API 호출이 서버에서 이루어짐) 데이터가 준비되면 QueryClient에 전달하여 root에서 hydration을 수행하기 때문에 컴포넌트에서 useSuspenseQuery를 호출하기만 하면 됩니다. CSR에서 react-query를 사용하듯이 코드가 엄청나게 간단해집니다. 이는 놀라운 개발 경험(DX)과 낮은 코드 복잡성을 제공합니다.

\n

그러나 이 방법을 선택하지 않은 이유는, 실험 단계 인점이 가장 컸고 깊게 중첩된 쿼리가 없고 요청 최적화를 잘 관리하고 있는 경우 사용하기를 권장하고 있는데 따로 잘 관리할 자신이 없었던 점이 제일 컸습니다. 조금 더 자세히 알아보고 싶으신 분들은 공식 문서와 라이브러리 코드를 참고하시면 좋을 것 같습니다 :)

\n

참고자료

\n\n","frontmatter":{"date":"January 07, 2024","title":"Next.js app router에서 React Query 사용하면서 고민했던 것들","categories":"react react-query","author":"soobing"},"fields":{"slug":"/react/next-app-router-react-query/"}},"prev":{"id":"49246cb3-c4b8-5aeb-a4c5-a1b737e89b51","html":"
\n

원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939

\n
\n

CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다.

\n

이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다.

\n

읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다.

\n

약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리즈 중 세 번째 글입니다. 관심이 있다면 특별한 순서 없이, 접근성을 고려하여 HTML 작성하기접근성을 고려하여 자바스크립트 작성하기를 지금이나 나중에 읽어보면 좋습니다.

\n
\n

제 첫 웹사이트를 만든 것은 약 17년 전이었고, 그때는 CSS가 아직 상대적으로 새로운 것이었습니다. 그 이후로 많은 것이 변했고, CSS는 이제 우리에게 웹을 스타일링하기 위한 놀라운 도구를 제공합니다. 우리는 Verdana에서 웹폰트로, 고정된 너비에서 반응형 웹 디자인으로, 테이블 기반 레이아웃에서 그리드로 넘어갔고, 이제는 border와 font 또는 shadow에 이미지를 사용할 필요가 없습니다. 우리는 사용자 정의 속성, 쿼리, calc() 및 많은 새로운 단위들을 가지고 있습니다. 물론 이것은 지난 몇 년간의 훌륭한 발전들 중 일부에 불과합니다.

\n

\"\"

\n

접근성을 고려하여 CSS 작성하기

\n

CSS를 사용하여 문제를 해결하는 무한한 방법과 다양한 속성이 우리의 삶을 더 쉽게 만들어주지만, 동시에 사용자의 경험을 악화시킬 수도 있습니다. 사실, 단 세 줄의 CSS 만으로 웹사이트에 접근하기 어렵게 만들 수 있습니다.

\n

이 글에서는 접근성 있는 CSS를 작성하는 데 도움이 될만한 기술과 고려 사항 그리고 접근방식을 모두 모았습니다. 이 컬렉션은 기본 개념과 잘 알려진 속성으로 시작하여, 끝에는 좀 더 새로운 것들을 다룹니다.

\n

예상했던 것보다 많은 내용을 담게 되어, 가장 관심 있는 섹션으로 바로 이동할 수 있도록 링크가 걸린 목차를 마련했습니다.

\n\n

즐겁게 읽어주세요!

\n

🔗 가독성 있는 텍스트에서 읽기 쉬운 텍스트로

\n

이미지, 아이콘, 동영상은 오늘날 웹 디자인에서 빼놓을 수 없는 요소이지만, 여전히 거의 모든 웹사이트에서는 텍스트가 콘텐츠의 대부분을 차지합니다. 텍스트는 어떤 기기에서든 읽을 수 있어야 하기 때문에, 폰트 속성을 스타일링하고, 테스트하며, 미세 조정하는 데 상당한 시간을 할애하는 것이 중요합니다.

\n

글꼴 크기 확대

\n

\"\"

\n

사용자가 화면에서 떨어진 거리에 따라 글꼴 크기는 확대해야 합니다 (출처: Marvel)

\n

한때 12px 글꼴 크기가 본문(body) 텍스트의 표준이었지만, 해상도가 높은 기기의 등장으로 평균 글꼴 크기는 한동안 15에서 18px 사이에 정착했습니다. 최근 몇 년간, 글꼴 크기는 다시 20px 이상으로 상승했으며, 이는 좋은 일입니다. 텍스트는 스마트폰에서 충분히 커야 하며, TV와 같은 큰 화면에서 멀리서도 읽을 수 있도록 화면 크기에 따라 확대해야 합니다.

\n

서체의 특성이 매우 다양하기 때문에 표준의 최소 크기를 정의하는 것은 의미가 없지만, 작은 화면 크기에 좋은 시작점은 아마도 18-20px일 것입니다.

\n

물론 글꼴 크기에 대해 더 많이 말할 수 있지만, 이 글에서 다루기에는 너무 많습니다. 자세한 내용은 Christian Miller당신의 Body 텍스트는 너무 작습니다를 읽어보시길 권장합니다.

\n

라인 높이(line-height) 설정

\n

브라우저의 기본 라인 높이는 대략 **1.2**입니다. 웹 콘텐츠 접근성 지침에 따르면, 텍스트 블록 내의 문단에서는 최소 **1.5**여야 합니다.

\n

\"\"

\n

*line-height가 1.2인 문단과 1.5인 문단 비교*

\n

문단 내 라인 높이가 조정된 텍스트는 가독성이 향상될 뿐만 아니라, 시각적으로도 꽤 더 매력적입니다.

\n

텍스트를 왼쪽 또는 오른쪽으로 정렬

\n

\"\"

\n

양쪽 정렬된 텍스트의 불규칙한 단어 간격

\n

양쪽 정렬이 왼쪽 또는 오른쪽 정렬된 텍스트보다 보기 좋다고 생각하는 사람들도 있지만, 이는 나쁜 관행으로 간주됩니다. text-align: justify는 같은 길이의 줄을 만들기 위해 단어 간격을 조정합니다. 이러한 불균일한 공백은 가독성을 해칠 수 있으며 매우 산만해질 수 있습니다. 필요한 경우 단어를 구분하는 것도 해결책이 될 수 있지만, CSS 하이픈은 잘 지원되지 않고 예상대로 작동하지 않을 수 있습니다.

\n

문단 너비 정의

\n

여러 출처에 따르면 디자이너들은 줄당 45에서 85자를 유지해야 한다고 합니다. 이상적인 문단 너비는 65자라고 여겨집니다.

\n

텍스트 블록의 너비를 정의할 때 ch 단위가 유용할 수 있습니다. 1ch는 숫자 0을 나타내는 문자의 너비와 동일합니다. 또한, font-family 또는 font-size가 변경되면 이에 따라 변경됩니다.

\n
p {\n  /* 최대 너비 65자 */\n  max-width: 65ch;\n}
\n

어떠한 종류의 반응형 타이포그래피 기술을 사용한다면, 매우 큰 화면에서 사이트를 테스트해야 합니다. 글꼴 크기에 제한이 없다면, 특정 뷰포트 크기에서 텍스트가 읽기 어려워질 수 있습니다. 제한을 설정하는 방법이나 반응형 타이포그래피에 익숙하지 않다면, Mike Riethmullers의 글 반응형 타이포그래피에 대한 정밀한 제어를 읽어보시기 바랍니다.

\n

🔗 가상 요소에 콘텐츠 신중하게 사용하기

\n

우리는 ::before::after라는 가상 요소를 사용하여 요소의 맨 처음이나 맨 끝에 CSS를 추가할 수 있습니다. 이것은 디자인 요소를 우리 컴포넌트에 추가하는 매우 일반적이고 편리한 방법을 제공하지만, content 속성을 사용하여 내용을 추가하는 것도 가능합니다. 관심사의 분리의 관점에서 보면, 우리는 이렇게 하지 않아야 합니다.

\n
h2 {\n  content: "DON'T DO THIS";\n}
\n

우리의 내용은 HTML 파일, 데이터베이스 또는 API에서 오는 것이지, CSS에서 오는 것이 아닙니다. 때때로 우리는 폰트 아이콘 또는 특수 문자와 같은 텍스트가 아닌 콘텐츠를 추가하기 위해 content 속성을 사용합니다. 그렇게 할 때, 일부 스크린 리더가 생성된 콘텐츠를 인식하고 설명한다는 것을 기억해야 합니다. 생성된 콘텐츠가 순전히 표현적인 경우 보조 기술에서 숨겨야 합니다. 예를 들어 aria-hidden을 사용할 수 있습니다.

\n
<span class="icon icon-key" aria-hidden="true"></span>
\n

🔗 화면만이 유일한 매체가 아니다

\n

우리가 디지털 시대에 살고 있음에도 불구하고, 사람들은 여전히 물건을 인쇄합니다. 당신의 페이지가 인쇄되거나 PDF로 저장될 때도 접근성이 좋고 사용하기 쉬워야 합니다. 우리가 해야 할 일은 CSS에 @media 블록을 추가하여 종이에 어울리지 않거나 의미가 없는 요소(네비게이션 또는 광고)들을 숨기거나 스타일을 조정하는 것입니다.

\n
@media print {\n  .header {\n    position: static;\n  }  nav {\n    display: none;\n  }\n}
\n

인쇄된 웹 페이지의 문제 중 하나는 링크가 완전히 쓸모없다는 것입니다. 왜냐하면 그것들이 어디로 이끄는지 알 수 없기 때문입니다. 다행히도 CSS는 속성의 값들을 드러내고 화면(이 경우에는 종이)에 표시하는 방법을 제공합니다.

\n
@media print {\n  a[href^="http"]:not([href*="mywebsite.com"])::after {\n    content: " (" attr(href) ")";\n  }\n}
\n

위의 코드는 href 속성이 있고 http로 시작하지만 mywebsite.com이 값에 포함되지 않은 모든 링크 옆에 href 속성의 값을 표시합니다.

\n

Firefox와 특히 Chrome은 인쇄용 스타일 시트를 테스트하고 디버깅하기 위한 도구를 제공합니다.

\n

더 깊이 파고들고 싶다면, 인쇄 스타일 작업을 위한 팁과 트릭들을 모아놓은 것이 있습니다.

\n

🔗 완전히 지원되지 않는 속성 값에 대한 대안

\n

가끔 우리는 특정 속성 값을 사용하고 싶지만, 일부 브라우저에서 지원하지 않기 때문에 사용할 수 없는 상황에 처합니다. 하지만 대안을 제공하는 한, 그것을 사용하는 것을 멈출 필요는 없습니다. 종종 쿼리나 다른 탐지 기능을 사용하지 않고도 할 수 있습니다.

\n

예를 들어, IE와 이전 버전의 Edge가 이해하지 못하는 vmax 단위를 사용하고 싶다고 가정해 봅시다.

\n
div {\n  width: 50vmax; /* IE와 Edge 이전 버전에서 작동하지 않음 */\n}
\n

대안을 제공하기 위해서, 덜 이상적이지만 브라우저가 이해할 수 있는 width 속성을 사용하면 됩니다. 예를 들어 width: 50vw처럼. 다음 줄에서 실제 원하는 값을 설정합니다.

\n
div {\n  width: 50vw;\n  width: 50vmax;\n}
\n

vmax를 이해하지 못하는 브라우저는 width: 50vw를 해석하고 width: 50vmax는 무시합니다. 반면에 vmax를 이해하는 브라우저는 먼저 width: 50vw를 해석한 다음 width: 50vmax를 해석합니다. vmax 선언이 vw 선언 다음에 오기 때문에, 유저는 vmax 로 설정한 버전이 적용 됩니다.

\n

🔗 콘텐츠를 숨기는 여러 가지 방법

\n

HTML의 제목들은 문서(document)의 개요를 잡는 데 매우 유용합니다. <h1>부터 <h6>까지의 제목을 사용함으로써, 브라우저와 다른 소프트웨어에게 문서가 어떻게 구성되어있고 각 부분들이 어떻게 연관되어 있는지 알려줍니다. 문서 개요를 가지는 것은 매우 중요합니다, SEO에 좋고 스크린 리더 사용자들이 사이트를 탐색하는 데 도움이 됩니다. 디자인에 제목이 없어도 제목이 있으면 좋을 것 같은 경우가 발생할 수 있습니다. 그것은 종종 디자인 자체가 구조를 전달할 때의 경우입니다. 이런 경우에는 마크업에서 제목을 단순히 제거하지 않고 시각적으로 숨깁니다. CSS가 있든 없든 문서의 구조가 명확해야 합니다.

\n

이것은 물론 단 하나의 예일 뿐이며, 폼에서 라벨을 시각적으로 숨기는 것은 또 다른 예입니다(UX 관점에서 라벨을 숨기는 것은 바람직하지 않습니다).

\n

CSS에서는 콘텐츠를 숨기는 여러 가지 방법이 있으며, 적절한 기술을 올바른 시나리오에 맞게 선택하는 것은 여러분에게 달려 있습니다.

\n

모든 사람으로부터 콘텐츠 숨기기

\n

hidden 속성을 사용하거나 visibility: hidden 또는 display: none을 설정함으로써 콘텐츠를 완전히 숨길 수 있습니다. 사용자는 볼 수 없으며 스크린 리더나 검색 엔진도 읽을 수 없습니다.

\n

시각적으로만 콘텐츠 숨기기

\n

시각적으로만 콘텐츠를 숨기는 것은 간단하지 않습니다. 스크린 리더는 여전히 접근 가능하게 해야 하며, 브라우저의 특이점을 다뤄야 하고 요소가 포커스될 때 무슨 일이 발생하는지 결정해야 합니다. 물론, 이미 다른 사람들이 이를 해냈고 해결책이 있습니다.

\n

제가 연구를 해본 결과, 많은 다양한 접근 방식이 있다는 것을 알게 되었습니다. 그래서 전문가들에게 의견을 물어보았고, 추천된 기술을 분석하여 무슨 일이 일어나는지 완전히 이해했습니다.

\n
.visually-hidden {\n  /* 일반적인 흐름에서 항목을 제거 */\n  position: absolute;\n  /* 잘못 발음되거나 뭉개지는 텍스트를 위한 해결책 */\n  white-space: nowrap;\n  /* 가능한 가장 작은 크기로 설정 (일부 화면 낭독기는 높이와 너비가 0인 요소를 무시함) */\n  width: 1px;\n  height: 1px;\n  /* 크기 조정 후 넘치는 콘텐츠 숨기기 */\n  overflow: hidden;\n  /* 요소의 크기를 변경할 수 있는 모든 속성 초기화 */\n  border: 0;\n  padding: 0;\n  /* 클리핑은 요소의 어떤 부분이 표시될지 정의함. */\n  /* 구식 clip 속성은 구형 브라우저를 위함 */\n  clip: rect(0 0 0 0);\n  /* 최신 브라우저를 위한 clip-path. inset(50%)는 콘텐츠를 사라지게 하는 내부 사각형을 정의함. */\n  clip-path: inset(50%);\n  /* 현재 아무도 정확히 왜 margin: -1px이 있는지 확실하지 않음. 게다가 이것이 문제를 일으킬 수 있음 (참조:https://github.com/h5bp/html5-boilerplate/issues/1985). */\n  margin: -1px;\n}
\n

이 클래스를 어딘가에 저장하고 시각적으로 콘텐츠를 숨기면서 보조 기술과 검색 엔진은 접근 가능하게 하고 싶을 때 사용하세요.

\n

스킵 링크

\n

앞의 클래스들은 스킵 링크로 사용하기에도 적합합니다. 스킵 링크는 초기에는 시각적으로 숨겨져 있지만 포커스될 때 보이는 링크입니다. 스크린 리더와 키보드 사용자들이 소개 콘텐츠를 건너뛰고 주요 콘텐츠로 바로 이동할 수 있도록 페이지의 첫 번째 항목 중 하나여야 합니다. 기본적으로 사용자를 페이지의 특정 부분으로 이동시키는 앵커 링크입니다.

\n

\"\"

\n

“Skip to content” 링크는 포커스될 때 보입니다

\n

코드 펜에서 직접 시도해보고, Tab을 눌러 건너뛰기 링크를 드러내보세요.

\n

의미론적으로 콘텐츠 숨기기

\n

때때로 시각적으로 콘텐츠를 표시하되 스크린 리더에서는 숨기는 것이 의미가 있습니다. 예를 들어 아이콘을 사용할 때 그렇습니다. 그런 경우에는 숨기고자 하는 요소에 aria-hidden 속성을 추가하고 true로 설정하세요.

\n
<button>\n  <span class="icon icon-hamburger" aria-hidden="true"></span>\n  <span class="text">Menu</span>\n</button>
\n

기타

\n

콘텐츠를 숨기는 다른 방법들도 있습니다. 예를 들어 음수 text-indent나 제로 font-size 또는 height 등입니다. 일부는 작동하지만 특정 주의사항이 있습니다. 자세한 내용은 webaim.org텍스트 숨기기 기술을 읽어보세요.

\n

🔗 나쁜 대비는 신뢰할 수 없다

\n

우리의 디자인은 가독성을 위해 텍스트와 배경 사이에 충분한 대비를 제공해야 합니다. 저시력자뿐만 아니라 시각 장애가 없는 사람들도 고대비에서 이익을 얻습니다. 맑은 날에 스마트폰을 밖에서 사용하는 것을 생각해 보세요.

\n

색상 대비란 무엇이며 왜 중요한가

\n

세계보건기구(World Health Organization)에 따르면 인구의 약 4%가 시각 장애가 있습니다. 남성의 7~12%와 여성의 1% 미만이 어떤 형태의 색상 시력 결함을 가지고 있습니다. 이러한 장애 중 많은 것이 대비에 대한 민감도를 감소시키고, 일부 경우에는 색상을 구별하는 능력까지 줄입니다.

\n

두 색상이 색상 휠의 다른 부분에서 올 때, 그 색상들은 대비를 이룹니다. 일반적으로 말해서, 두 색상의 차이가 클수록 대비가 높습니다. 웹 디자이너 및 개발자로서 우리에게는 단순히 대비 자체뿐만 아니라, 텍스트에 적용됐을 때 얼마나 잘 작동하는지가 중요합니다. 텍스트와 그 배경 사이의 대비는 적어도 중등도 저시력을 가진 사람들이 읽을 수 있을 정도로 높아야 합니다. 물론, 이 기준을 충족하는지 고민할 필요는 없습니다. 웹 접근성 이니셔티브(Web Accessibility Initiative, WAI)는 이를 측정하기 위한 비율을 정의했습니다.

\n

최소 대비 비율

\n

대비 비율은 특정 배경에서 특정 크기와 너비를 가진 텍스트의 대비가 얼마나 높은지를 알려줍니다. 비율은 1:1에서 21:1까지 다양할 수 있습니다. 비교된 두 색상이 동일하면 1:1이고, 검정과 흰색이 대립되는 경우 21:1입니다.

\n

\"\"

\n

#777777 색상의 텍스트가 #DDDDDD 배경에서 3.3:1 비율을 가집니다. (출처: 대비 비율)

\n

웹 콘텐츠 접근성 지침(WCAG) 2.0에 따르면, 배경과 그 텍스트(또는 텍스트 이미지) 사이에 최소 4.5:1의 대비 비율이 존재해야 합니다. 이는 24px 미만(굵지 않은 경우) 및 19px 미만(굵은 경우)인 텍스트에 적용됩니다. 더 큰 텍스트의 경우 3:1 비율이면 충분합니다. 이것들은 레벨 AA 기준을 충족하기 위한 최소 수치입니다. 레벨 AAA를 통과하려면 일반 텍스트의 최소 비율은 7:1이고 굵은 텍스트는 4.5:1입니다. 규정 준수를 위한 필수 사항은 아니지만, 우리가 아이콘을 사용한다면 텍스트 대비 규정을 충족하는 아이콘을 사용해야 합니다.

\n

저는 제 친구 Daniel에게 비율에 대해 말했고, 우리가 현재 작업 중인 프로젝트에서 그것을 올바르게 가져가는 것이 중요하다고 말했습니다. 다양한 조합을 시도한 후, 그는 이것이 생각했던 것보다 어렵다고 전화로 말했습니다. 문제는 충분히 시각적으로 매력적인 조합이 없는 것이 아니라, 지난 몇 년 동안 디자이너들이 저대비 조합을 사용하는 데 익숙해졌다는 것입니다. 작은 에이전시뿐만 아니라 애플이나 구글과 같은 큰 회사들도 이 불리한 디자인 추세를 따르고 있습니다.

\n
\n

나이가 확실히 내 시력에 영향을 미쳤지만, 나는 디자인 트렌드로 고통받고 있다.

\n
\n

Kevin Marks

\n

대비 비율을 계산하기 위한 공식이 있지만, 오래된 계산기를 꺼내서 계산할 필요는 없습니다. 도구들이 있습니다.

\n

대비 비율 측정

\n

Chrome Canary에서는 개발자 도구에서 직접 대비 비율을 표시할 수 있습니다. Remy Sharp가 블로그 글에서 이를 공유합니다.

\n

\"\"

\n

Chrome 개발자 도구에서의 대비 비율.

\n

색상 대비와 일반적인 접근성을 테스트하기 위한 많은 도구들이 있습니다. 다음 목록은 광범위하지는 않지만, 제가 선호하는 도구들의 작은 모음집 입니다.

\n

온라인

\n\n

브라우저에서 빠르고 쉬운 대비 체커

\n\n

브라우저에서 좀 더 많은 옵션을 가진 대비 체커

\n\n

브라우저 도구로 대비와 더 많은 것들을 체크

\n\n

자동 대비 체크가 있는 색상 선택기

\n

브라우저 확장 프로그램과 개발자 도구

\n\n

Chrome 60은 Lighthouse를 기반으로 한 새로운 감사 패널과 함께 출시되었습니다. 접근성 점수를 부여하고 문제를 나열합니다.

\n\n

대비, 문서 개요 등을 테스트하기 위한 훌륭한 브라우저 확장 프로그램.

\n\n

aXe Chrome 확장 프로그램을 사용하여 웹 사이트에서 접근성 결함을 자동으로 찾는 도구.

\n

기타

\n\n

“WCAG에 대한 두 레이어의 색상 대비를 계산하는 Sketch 플러그인.

\n\n

고대비의 경험

\n

고대비의 색상을 사용하는 것도 훌륭하지만, 저시력을 가진 사람들은 여전히 웹사이트에서 사용하는 색상을 변경하고 싶어할 수 있습니다. 사용자의 요구는 매우 다양하며 그에 따라 색상 변경 방법도 다양합니다. 이 사실은 어느 정도 예측 불가능성을 내포하고 있으며, 우리의 페이지들이 항상 완전한 접근성을 보장하기 어렵게 만듭니다. 그래서 우리는 단지, 대비 수준 AA 또는 AAA 기준을 충족하는 것에만 의존해서는 안 되며, 웹사이트를 철저히 테스트하고 대비가 높은 대안을 제공하는 것도 고려해야 합니다.

\n

윈도우에서의 고대비 모드

\n

윈도우에서는 설정에서 고대비 옵션을 사용할 수 있습니다. 사용자는 자신만의 색상 설정을 정의하거나 사전 정의된 테마를 선택할 수 있습니다.

\n

\"\"

\n

윈도우에서의 고대비 설정

\n

간단한 로그인 폼을 만들었고(첫 번째 스크린샷 중 하나. https://dribbble.com/shots/1687064-Simple-Login-Form으로 부터 영감을 받음.) 고대비를 가진 다양한 테마로 테스트해 보았습니다.

\n

\"\"

\n

고대비 설정에서의 다양한 로그인 폼

\n

Anika Henke는 사용자들이 웹사이트에서 색상을 어떻게 변경하는지에 대해 썼습니다. 그녀는 폼을 테스트하던 중 입력 필드가 보이지 않게 되었고 버튼이 인식되지 않게 되었다는 것을 발견했습니다. 위 스크린샷에서도 같은 일이 발생하는 것을 볼 수 있습니다. 대체 텍스트가 없었다면, 사용자들은 두 개의 입력 필드가 있다는 것을 알지 못했을 것입니다. 인풋과 버튼에 기본 테두리를 추가하는 것은 빠른 해결책이었습니다(브라우저 간 테스트되지 않음).

\n

\"\"

\n

고대비 설정에서 인풋과 버튼에 테두리가 있는 개선된 로그인 폼

\n

미디어 쿼리를 사용하여 고대비 모드가 활성화되었는지 감지하고 특정 스타일을 제공할 수 있습니다.

\n
/* 고대비 모드 활성화 */\n@media (-ms-high-contrast:active) {\n}/* 특정 검정-백색 테마의 고대비 모드 */\n@media (-ms-high-contrast:black-on-white) {\n}/* 특정 검정-백색 테마의 고대비 모드 */\n@media (-ms-high-contrast:white-on-black) {\n}
\n

Patrick H. Lauke 는 윈도우 고대비 모드: -ms-high-contrast의 제한된 유용성에서 이 미디어 기능에 대한 그의 생각과 우려를 공유했습니다. 이에 응답으로 Greg Whitworth는 다음과 같이 지적했습니다. “이 기능의 유일한 목적은 대비 민감도를 가진 사용자들에게 더 나은 경험을 제공하는 것입니다. 그러므로, 특정 색상이 무엇인지에 대해 반드시 신경 쓸 필요는 없습니다. 어느 정도까지는, 여러분의 사이트가 어떻게 보이는지보다 어떻게 고대비에서 기능하는지에 대해 신경 써야 합니다.”

\n

높은 대비 크롬 확장 프로그램

\n

구글 크롬을 위한 고대비 확장 프로그램도 있어, 사용자들이 텍스트를 읽기 쉽게 만드는 여러 고대비 색상 필터로 웹을 탐색할 수 있습니다.

\n

고대비의 대안

\n

디자인의 일부분이 충분한 대비를 가지고 있지 않더라도, Alternate Version 조항을 사용하여 WCAG 기준을 충족할 수 있습니다. 이에 따르면, 사용자에게 페이지의 고대비 버전으로 연결하는 링크나, 페이지의 모든 측면이 준수하도록 페이지를 변경할 수 있는 페이지 상의 컨트롤을 제공해야 합니다.

\n

이 대안에 대한 몇 가지 기준이 있습니다.

\n\n

NoCoffee로 테스트하기

\n

\"\"

\n

NoCoffee는 저시력, 색상 결핍 및 차단된 시각 영역을 시뮬레이션합니다

\n

기준을 충족하는 것과 실제 사람을 대상으로 테스트하는 것은 별개의 문제입니다. 모든 사람이 전문적인 테스트 수단을 가지고 있는 것은 아닙니다. 다행히도, NoCoffee는 저시력, 색상 결핍 및 차단된 시각 영역을 쉽게 시뮬레이션할 수 있는 방법을 제공합니다. 이는 경미한~극심한 시력 문제를 가진 사람들이 직면하는 문제를 이해하는 데 도움이 될 수 있습니다.

\n

🔗 색상이 정보의 유일한 단서가 되어서는 안 된다

\n

앞서 언급했듯이, 많은 남성들이 시력 결함을 가지고 있습니다. 유형도 다양합니다. 가장 흔한 유형 중 하나인 중색 이상(Deuteranomaly)은 빨강과 녹색을 구분하기 어렵게 만듭니다. 색상 시력 결함이 있는 사람은 인터페이스를 사용할 수 없게 될 수 있으므로 색상만을 시각적 단서로 사용하는 것을 피해야 합니다.

\n

이전 예시에서 보여준 폼의 입력 필드에 성공 및 오류 상태를 나타내는 테두리를 추가했습니다. 다음 스크린샷은 색상만으로 사용자에게 충분한 피드백을 주지 못한다는 것을 보여줍니다. 테두리 색상이 전혀 보이지 않거나 잘못 보이는 경우가 있습니다.

\n

\"\"

\n

고대비 모드에서 색상만으로 폼의 성공과 실패를 구별하는 것은 효과적이지 않습니다.

\n

간단한 아이콘을 추가하면 접근성과 사용자 경험을 개선하는 데 도움이 될 수 있습니다.

\n

링크도 비슷한 예시 중 하나입니다. 링크는 색상만으로 일반 텍스트와 구별되어서는 안 됩니다. 링크에 밑줄을 유지하는 것이 좋습니다.

\n

🔗 순서에 신경 쓰기

\n

아이템들을 배치 순서를 바꾸는 방법은 많이 있습니다. 예를 들어, Flexbox에는 orderflex-direction이 있고, Grid에는 order, flex-auto-flow 및 명시적 배치가 있습니다. 이러한 속성들은 매우 유용하지만, 콘텐츠의 DOM 순서와 시각적 표현 사이의 연결이 끊어질 수 있습니다.

\n

다음 예시에서는 여러 그리드 속성을 사용하여 배치된 갤러리의 이미지를 볼 수 있습니다.

\n\n

처음에는 문제가 없어 보이지만, 키보드를 사용하여 이미지에서 이미지로 이동할 때 순서를 전혀 예측할 수 없다는 것을 알 수 있습니다. Tab 키를 눌렀을 때 다음에 어떤 이미지가 강조될지 알 수가 없습니다. 여기에 포커스 스타일이 없으면 최악의 상황이 될 수 있습니다.

\n\n

예측 불가능하거나 잘못된 순서는 키보드 사용자뿐만 아니라 스크린 리더 사용자에게도 문제가 됩니다. 스크린 리더는 DOM 순서대로 콘텐츠를 표시하므로 소프트웨어는 CSS 순서에 영향을 받지 않지만 사용자는 영향을 받습니다. 스크린 리더 사용자는 콘텐츠의 시각적 표현에 신경 쓰지 않는다고 생각할 수 있지만, 모든 스크린 리더의 사용자가 시각 장애가 있는 것은 아닙니다. 일부는 저시력이나 학습 장애가 있어 화면에 표시되는 내용을 보완하기 위해 스크린 리더를 사용합니다.

\n

이 순서 문제는 플렉스나 그리드 아이템뿐만 아니라 모든 종류의 위치 지정에도 적용됩니다. 스타일 없이도 의미가 있는 방식으로 콘텐츠를 정렬하는 것이 중요하며, 디자인에서의 순서와 일치하는지 확인해야 합니다. 일치하지 않으면 디자인을 다시 생각해야 할 수 있습니다. CSS에서 올바르게 위치시키지 못한다고 해서 마크업에서 요소들을 임의로 재배열하지 마세요.

\n

Rob Dodson’s의 콘텐츠 재배열이 접근성에 영향을 미치는가?와 Adrian Roselli의 코드의 순서가 중요하다 글을 참고해 더 자세한 내용을 알아보세요.

\n

🔗 중요한 것에 집중하기: focus

\n

저는 이미 키보드 탐색 기초와 포커스 가능한 요소들에 대해 접근성을 고려하여 자바스크립트 작성하기라는 글을 썼습니다. 이 주제가 처음이시라면, 그 글을 빠르게 읽고 오세요.

\n

키보드를 사용하여 웹사이트를 탐색할 수 있도록 하는 것은 중요합니다. 많은 사용자들이 웹 서핑을 할 때 키보드에 의존합니다. 그들 중에는 운동 장애가 있는 사람들, 시각 장애가 있는 사람들, 손이 없거나 어떤 이유로든 마우스나 트랙패드를 사용할 수 없는 사람들이 있습니다.

\n

CSS를 사용해서 포커스 가능한 요소들에 스타일을 적용할 수 있는 몇 가지 방법이 있습니다.

\n

포커스된 아이템 선택하기**

\n

:focus 의사 클래스를 사용하여 포커스 가능한 아이템들이 포커스 되었을때 스타일을 적용할 수 있습니다.

\n
a:focus {\n  background-color: #000000;\n  color: #FFFFFF;\n}
\n

기본 포커스 스타일은 브라우저마다 일관성이 없으며 보기 좋지 않은 경우가 많으며, 때로는 디자인과 잘 어울리지 않습니다. 사용자 경험을 개선하고 디자인에 맞는 맞춤 포커스 스타일을 제공하는 것이 좋습니다.

\n

하지만 무엇을 하든, 대체 스타일을 제공하지 않고 기본 윤곽선(점선 윤곽, 파란색 또는 주황색 반지)만 제거하지 마세요. 주로 키보드로 탐색하는 사용자들은 포커스 위치를 알 수 없다면 사이트를 사용할 수 없게 됩니다.

\n

\"\"

\n

대안 없이 기본 포커스 스타일을 제거하지 마세요 (출처: outlinenone.com)

\n

이것은 단순한 팁이 아니라 레벨 AA 기준 기준입니다.

\n

키보드와 마우스 사용자 구분하기

\n

이미 언급했듯이, 디자이너들이 좌절하는 것 중 하나는 브라우저 간 포커스 스타일의 일관성이 부족하다는 것입니다. 또한 마우스를 사용할 때도 일부 포커스 가능한 요소들에서 포커스 스타일이 보이는 점도 문제입니다. 때로는 마우스 사용자에게는 포커스 스타일을 보여줄 필요가 없으며, 심지어 방해가 되거나 미학적으로 불쾌할 수 있습니다.

\n

\"\"

\n

컨텐츠 영역이 클릭되었을 때 크롬에서 파란색 윤곽선이 보이는 맞춤 탭 컴포넌트 (출처: frend.co)

\n

웹 페이지의 특정 요소에 포커스가 가 있을 때 outline 속성을 제거해서는 안됩니다. 왜냐하면 컴포넌트가 더 이상 키보드 사용자에게 접근 불가능하기 때문입니다. 우리에게 필요한 것은 키보드와 마우스 사용을 구별하는 방법입니다. 이것은 CSS Level 4 선택자 명세의 일부인 :focus-ring 가상 클래스를 사용하여 가능 합니다. ”:focus-ring 가상 클래스는 요소가 :focus 가상 클래스와 일치하고, 사용자 에이전트(브라우저)가 휴리스틱(규칙이나 패턴)을 통해 요소에 특별히 표시되어야 함을 결정할 때 (일반적으로 ‘focus-ring’을 통해) 적용됩니다.” (출처: CSS 선택자 레벨 4 초안)

\n
/* 기본 윤곽선 제거 */\n:focus {\n  outline: none;\n}\n\n/* 윤곽선이 보여야 할 때만 추가 */\n:focus-ring {\n  outline: 2px solid blue;\n}
\n

아쉽게도 현재 어떤 브라우저도 :focus-ring의 표준 구현을 지원하지 않습니다. (Firefox는 -moz-focus-ring을 지원함), 하지만 적절할 때 .focus-ring 클래스를 추가하는 경량 폴리필이 있습니다.

\n
/* 자바스크립트가 활성화되어 작동하고, .focus-ring 클래스가 없는 \n모든 포커스 가능한 요소를 선택하여 윤곽선을 제거합니다 */\n.js-focus-ring :focus:not(.focus-ring) {\n    outline-width: 0;\n}
\n

자세한 내용은 Rob Dodson의 a11ycasts 에피소드, Focus Ring!을 시청하세요.

\n

포커스된 자식이 있는 요소에 대한 스타일링

\n

:focus-within는 상대적으로 새로운 가상 클래스로, 이미 대부분의 주요 브라우저에서 지원되고 있습니다. 이 클래스를 사용하면 현재 포커스된 자식 요소를 가진 요소를 선택할 수 있습니다.

\n

\"\"

\n

자식 항목 중 하나가 포커스되면 그림자가 표시되는 예시입니다.

\n
form:focus-within {\n  box-shadow: 0 0 4px 6px rgba(80,88,156,0.2);\n}
\n

이 기능을 CodePen에서 확인할 수 있습니다.

\n

포커스에 대한 자세한 내용은 YouTube에서 포커스란 무엇인가요? 영상을 참조하세요.

\n

🔗 그리드와 평평한 문서 구조

\n

새로운 사이트를 만들 때, 우리는 보통 HTML 작성부터 시작합니다. 적절한 마크업을 선택하고 요소들을 논리적 순서에 맞게 배치합니다. 문서가 올바르게 작성되어 있고 잘 구조화되어 있으며 순서가 의미 있게 되면 CSS를 추가합니다. CSS Grid Layout이 나오기 전에는, 특히 DOM 순서와 디자인 순서가 일치하지 않은 경우에 레이아웃을 만드는 것이 매우 까다로웠습니다. float, position 그리고 때때로 Flexbox조차도 어떤 상황에서는 충분히 유연하지 않았고, 우리는 DOM 순서를 변경하고 싶은 유혹에 빠지곤 했습니다. Grid의 명시적 배치와 해당 영역 덕분에 아이템을 위치시키는 데 필요한 모든 유연성을 갖게 되었습니다. 이것은 훌륭하지만, Grid는 문서 구조를 해칠 수 있는 새로운 유혹을 도입합니다.

\n

다음과 같은 디자인을 가지고 있고, 그 아이템들에 h2ul을 사용한다고 가정해 봅시다. 왜냐하면 그것이 당신에게 가장 의미가 있기 때문입니다.

\n

\"\"

\n

제목과 목록이 있는 레이아웃

\n
<div class="wrapper">\n  <h2>Heading</h2>\n  <ul>\n    <li><a href="#">Element 1</a></li>\n    <li><a href="#">Element 2</a></li>\n    <li><a href="#">Element 3</a></li>\n    <li><a href="#">Element 4</a></li>\n    <li><a href="#">Element 5</a></li>\n    <li><a href="#">Element 6</a></li>\n  </ul>\n</div>
\n

이러한 요소들을 열에 넣고 <h2>를 위치시키는 것은 꽤 쉽습니다… 또는 적어도 그렇게 보입니다.

\n
.wrapper {\n  display: grid;\n  grid-template-columns: 120px repeat(2, 1fr);\n  grid-gap: 20px;\n}h2 {\n  grid-column: 2 / -1;\n}
\n

\"\"

\n

제목과 목록이 있는 레이아웃. 그리드 컨테이너의 직접적인 자식만 그리드에 배치됩니다.

\n

하지만 예상과는 달리 보입니다. 문제는 그리드 컨테이너의 직접적인 자식만이 그리드에 배치된다는 것인데, 이 경우에는 <h2><ul>이 해당됩니다. 하지만 여러분은 <li>들을 그리드 아이템으로 만들고 싶습니다. 이 문제에 대한 최악의 해결책은 구조를 단순화시켜 <ul>을 제거하고 <li><div>로 변환하여 그리드 컨테이너의 직접적인 자식으로 만드는 것입니다.

\n

가장 좋은 해결책은 <ul>display 속성을 subgrid로 설정하는 것이지만, 불행히도 subgrid는 명세의 레벨 1에 포함되지 않았고 우리는 그것이 출시될 때까지 더 기다려야 합니다.

\n

<ul>display: contents를 사용할 수 있지만, 현재 Firefox만이 이를 지원합니다. display: contents는 요소의 자식들이 마치 요소의 부모의 직접적인 자식인 것처럼 보이게 만듭니다.

\n

결국, **<ul>**에 다른 그리드를 정의해야 합니다. 이것은 이상적이지 않지만, 문서의 구조를 단순화시키고 의미를 해치는 것보다는 낫습니다. 이것은 매우 기본적인 예제이며 목록이 전체 그리드를 차지하기 때문에 부모 그리드에서 일부 값을 상속받을 수 있습니다.

\n
ul {\n  /* 전체 그리드를 차지함 */\n  grid-column: 1 / -1;  /* 다른 그리드를 생성하고 부모 그리드에서 값을 상속받음 */\n  display: inherit;\n  grid-template-columns: inherit;\n  grid-gap: inherit;  /* display: contents를 이해하는 브라우저를 위해 display 덮어쓰기 */\n  display: contents;\n}
\n

CodePen에서 두 가지 솔루션을 볼 수 있습니다.

\n

결론

\n

이 글에서 꽤 많은 내용을 다루고 있지만 CSS와 접근성에 대해 알아야 할 모든 것을 다루지는 않습니다. 그러나 이것은 단순한 출발점 그 이상입니다. DOM 및 포커스 순서를 올바르게 설정하고 고대비에 신경 쓰고 일반적으로 접근성을 고려하여 디자인 하는 것만으로도 이미 훌륭한 작업을 하고 있는 것입니다. 새로운 페이지나 사이트를 만들 때마다 접근성을 약간만 더 고려한다면 웹을 더 나은 곳으로 만들 수 있습니다.

\n
\n

제약사항을 고려하여 디자인하는 것은 단순히 잘 디자인하는 것이다.

\n
\n

Aaron Gustafson

\n

이 글을 즐겁게 읽으셨고 새로운 것을 배우셨기를 바랍니다. 질문이나 피드백이 있으시면 댓글을 남겨주시거나 트위터를 통해 연락해 주시기 바랍니다.

\n

이 글을 작성하는 데 도움을 주신 멘토 Aaron Gustafson에게 감사드립니다.

\n

더 많은 접근성 팁

\n

이 글은 네 부분 시리즈 중 세 번째입니다. 마지막 글은 준비 중이며 곧 공개될 예정입니다.

\n
    \n
  1. 접근성을 고려한 HTML 작성하기
  2. \n
  3. 접근성을 고려한 JavaScript 작성하기
  4. \n
  5. 접근성을 고려한 CSS 작성하기
  6. \n
  7. 다음 글: 접근성을 고려한 디자인 및 개발 방법 배우기
  8. \n
\n

독자 여러분의 독서와 이 글을 좋아하시고 공유해 주시면 감사하겠습니다.

\n

제가 작성한 다른 글도 확인해보실 수 있습니다.

\n

Progressively Enhancing CSS Layout: From Floats To Flexbox To Grid

\n

The Difference Between Explicit and Implicit Grids

\n

추가적인 읽을 거리와 자료들

\n

가독성 있는 텍스트 작성법

\n\n

가상 요소에 콘텐츠 신중하게 사용하기

\n\n

화면만이 유일한 매체가 아니다

\n\n

완전히 지원되지 않는 속성 값에 대한 대안

\n\n

콘텐츠를 숨기는 여러 가지 방법

\n\n

나쁜 대비는 신뢰할 수 없다

\n\n

색상이 정보의 유일한 단서가 되어서는 안 된다

\n\n

순서에 신경 쓰기

\n\n

중요한 것에 집중하기: focus

\n\n

그리드와 평평한 문서 구조

\n\n","frontmatter":{"date":"February 18, 2024","title":"(번역) 접근성을 고려하여 CSS 작성하기","categories":"translate a11y","author":"soobing"},"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}},"site":{"siteMetadata":{"siteUrl":"https://soobing.github.io","comments":{"utterances":{"repo":"soobing/soobing.github.io"}}}}},"pageContext":{"slug":"/react/storybook-react-server-components/","nextSlug":"/react/next-app-router-react-query/","prevSlug":"/a11y/writing-css-with-accessibility-in-mind/"}}, "staticQueryHashes": ["1073350324","2938748437"]} \ No newline at end of file diff --git a/page-data/sq/d/1956554647.json b/page-data/sq/d/1956554647.json index 9247f825..23999640 100644 --- a/page-data/sq/d/1956554647.json +++ b/page-data/sq/d/1956554647.json @@ -1 +1 @@ -{"data":{"allMarkdownRemark":{"edges":[{"node":{"frontmatter":{"title":"‘의도적으로 무시’하는 코드에 대한 TypeScript와 ESLint의 다른 처리 방식","categories":"framework"},"fields":{"slug":"/typescript-eslint-handling-intentionally-ignored-variables/"}}},{"node":{"frontmatter":{"title":"Astro로 알아보는 Selective & Progressive Hydration","categories":"framework"},"fields":{"slug":"/astro-hydration/"}}},{"node":{"frontmatter":{"title":"환경 변수(process.env)를 구조 분해 할당하면 안되는 이유","categories":"react next"},"fields":{"slug":"/react/process-env-destructuring-error.md/"}}},{"node":{"frontmatter":{"title":"(번역) 타입스크립트에서 'As Const' 이해하기","categories":"translate typescript"},"fields":{"slug":"/typescript/typescript-as-const/"}}},{"node":{"frontmatter":{"title":"타입스크립트 타입 호환성 문제 해결하기 \"as const vs satisfies\"","categories":"typescript"},"fields":{"slug":"/typescript/as-const-vs-satisfies/"}}},{"node":{"frontmatter":{"title":"(번역) 스트리밍 HTML과 DOM 비교 알고리즘","categories":"translate react"},"fields":{"slug":"/react/html-node-streaming/"}}},{"node":{"frontmatter":{"title":"브라우저 위치 및 크기 관련 API들","categories":"browser"},"fields":{"slug":"/browser/browser-coordinate-size-api/"}}},{"node":{"frontmatter":{"title":"(번역) 접근성을 고려하여 CSS 작성하기","categories":"translate a11y"},"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}}},{"node":{"frontmatter":{"title":"docker에서 자주쓰는 명령어","categories":"infra"},"fields":{"slug":"/infra/docker-command/"}}},{"node":{"frontmatter":{"title":"(번역) 리액트 서버 컴포넌트를 위한 스토리북","categories":"translate react"},"fields":{"slug":"/react/storybook-react-server-components/"}}},{"node":{"frontmatter":{"title":"Next.js app router에서 React Query 사용하면서 고민했던 것들","categories":"react react-query"},"fields":{"slug":"/react/next-app-router-react-query/"}}},{"node":{"frontmatter":{"title":"(번역) 2024년 자바스크립트의 5가지 가장 혁신적인 제안","categories":"translate javascript"},"fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"}}},{"node":{"frontmatter":{"title":"서버에서 React Query prefetching 한 데이터 사용하기","categories":"react react-query"},"fields":{"slug":"/react/server-rendering-and-react-query/"}}},{"node":{"frontmatter":{"title":"(번역) 시그널(Signal)에 대한 소개","categories":"translate react"},"fields":{"slug":"/react/introducing-signals/"}}},{"node":{"frontmatter":{"title":"(번역) React에서 UI와 로직 분리하기:헤드리스 컴포넌트를 사용한 클린 코드 접근법","categories":"translate react"},"fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"}}},{"node":{"frontmatter":{"title":"(번역) 우리들을 위한 디자인 패턴","categories":"translate cs"},"fields":{"slug":"/cs/design-patterns-for-humans/"}}},{"node":{"frontmatter":{"title":"자바스크립트 가비지 컬렉션 알고리즘","categories":"javascript"},"fields":{"slug":"/javascript/garbage-collection/"}}},{"node":{"frontmatter":{"title":"(번역) React Query를 사용하여 서버 상태를 관리하는 방법","categories":"translate react react-query"},"fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"}}},{"node":{"frontmatter":{"title":"(번역) 캐시 시스템 설계할 때 기억해야 할 6가지 캐싱 전략","categories":"translate cs"},"fields":{"slug":"/cs/6-caching-strategies/"}}},{"node":{"frontmatter":{"title":"Proxy, Reflect와 메타프로그래밍","categories":"javascript"},"fields":{"slug":"/javascript/metaprogramming/"}}},{"node":{"frontmatter":{"title":"(번역) StructuredClone API를 사용하여 객체를 깊은 복사하는 법","categories":"translate javascript"},"fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"}}},{"node":{"frontmatter":{"title":"날짜 입력 input 인터렉션 개발기","categories":"feature"},"fields":{"slug":"/feature/input-date/"}}},{"node":{"frontmatter":{"title":"Recoil 도입기(feat. 폴더구조)","categories":"react"},"fields":{"slug":"/react/recoil-introduction/"}}},{"node":{"frontmatter":{"title":"React Query useMutation에서 variable 옵셔널하게 사용하기","categories":"react react-query troubleshooting"},"fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"}}},{"node":{"frontmatter":{"title":"React Query 시작하기 (feat. Tanstack)","categories":"react react-query"},"fields":{"slug":"/react/react-query-basic/"}}},{"node":{"frontmatter":{"title":"React의 상태관리 종류 4가지","categories":"react"},"fields":{"slug":"/react/react-state-management/"}}},{"node":{"frontmatter":{"title":"에러 핸들링에 대한 고민 (feat. React)","categories":"react"},"fields":{"slug":"/react/error-handle/"}}},{"node":{"frontmatter":{"title":"Touch, Mouse 이벤트 함께 다루기 (feat. dropdown)","categories":"feature browser"},"fields":{"slug":"/browser/touch-mouse-event/"}}},{"node":{"frontmatter":{"title":"Bitbucket과 Jenkins를 연동하여 테스트 자동화 (feat. Generic Webhook Trigger)","categories":"infra"},"fields":{"slug":"/infra/jenkins-bitbucket/"}}},{"node":{"frontmatter":{"title":"Promise를 사용하여 window.confirm 구현하기","categories":"feature"},"fields":{"slug":"/feature/confirm/"}}},{"node":{"frontmatter":{"title":"자바스크립트 테스트 개념정리","categories":"test"},"fields":{"slug":"/test/test-introduction/"}}},{"node":{"frontmatter":{"title":"로딩 indicator 구현시 고려해야할 접근성","categories":"a11y"},"fields":{"slug":"/a11y/loader/"}}},{"node":{"frontmatter":{"title":"모달 구현시 고려해야할 접근성","categories":"a11y"},"fields":{"slug":"/a11y/modal/"}}},{"node":{"frontmatter":{"title":"cypress 주요 api 모음","categories":"test"},"fields":{"slug":"/test/cypress-api/"}}},{"node":{"frontmatter":{"title":"postmessage를 이용하여 window 간에 통신하기","categories":"browser"},"fields":{"slug":"/browser/post-message/"}}},{"node":{"frontmatter":{"title":"PC용 브라우저(IE, Safari, Chrome, ...) 이슈 모음","categories":"browser"},"fields":{"slug":"/browser/pc-issues/"}}},{"node":{"frontmatter":{"title":"api 서비스 설계","categories":"feature"},"fields":{"slug":"/feature/api-service/"}}},{"node":{"frontmatter":{"title":"Internet Explorer 이슈 모음","categories":"browser"},"fields":{"slug":"/browser/ie-issues/"}}},{"node":{"frontmatter":{"title":"Gatsby 테마로 GitHub Blog 만들기","categories":"블로그"},"fields":{"slug":"/gatsby-github-blog/"}}},{"node":{"frontmatter":{"title":"쉽고 빠르게 나만의 개츠비(Gatsby) 블로그 만들기","categories":"블로그"},"fields":{"slug":"/gatsby-starter-zoomkoding-introduction/"}}}]}}} \ No newline at end of file +{"data":{"allMarkdownRemark":{"edges":[{"node":{"frontmatter":{"title":"‘의도적으로 무시’하는 코드에 대한 TypeScript와 ESLint의 다른 처리 방식","categories":"framework"},"fields":{"slug":"/typescript-eslint-handling-intentionally-ignored-variables/"}}},{"node":{"frontmatter":{"title":"Astro로 알아보는 Selective & Progressive Hydration","categories":"framework"},"fields":{"slug":"/astro-hydration/"}}},{"node":{"frontmatter":{"title":"환경 변수(process.env)를 구조 분해 할당하면 안되는 이유","categories":"react next"},"fields":{"slug":"/react/process-env-destructuring-error.md/"}}},{"node":{"frontmatter":{"title":"(번역) 타입스크립트에서 'As Const' 이해하기","categories":"translate typescript"},"fields":{"slug":"/typescript/typescript-as-const/"}}},{"node":{"frontmatter":{"title":"타입스크립트 타입 호환성 문제 해결하기 \"as const vs satisfies\"","categories":"typescript"},"fields":{"slug":"/typescript/as-const-vs-satisfies/"}}},{"node":{"frontmatter":{"title":"(번역) 스트리밍 HTML과 DOM 비교 알고리즘","categories":"translate react"},"fields":{"slug":"/react/html-node-streaming/"}}},{"node":{"frontmatter":{"title":"브라우저 위치 및 크기 관련 API들","categories":"browser"},"fields":{"slug":"/browser/browser-coordinate-size-api/"}}},{"node":{"frontmatter":{"title":"docker에서 자주쓰는 명령어","categories":"infra"},"fields":{"slug":"/infra/docker-command/"}}},{"node":{"frontmatter":{"title":"(번역) 접근성을 고려하여 CSS 작성하기","categories":"translate a11y"},"fields":{"slug":"/a11y/writing-css-with-accessibility-in-mind/"}}},{"node":{"frontmatter":{"title":"(번역) 리액트 서버 컴포넌트를 위한 스토리북","categories":"translate react"},"fields":{"slug":"/react/storybook-react-server-components/"}}},{"node":{"frontmatter":{"title":"Next.js app router에서 React Query 사용하면서 고민했던 것들","categories":"react react-query"},"fields":{"slug":"/react/next-app-router-react-query/"}}},{"node":{"frontmatter":{"title":"(번역) 2024년 자바스크립트의 5가지 가장 혁신적인 제안","categories":"translate javascript"},"fields":{"slug":"/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-2024/"}}},{"node":{"frontmatter":{"title":"서버에서 React Query prefetching 한 데이터 사용하기","categories":"react react-query"},"fields":{"slug":"/react/server-rendering-and-react-query/"}}},{"node":{"frontmatter":{"title":"(번역) 시그널(Signal)에 대한 소개","categories":"translate react"},"fields":{"slug":"/react/introducing-signals/"}}},{"node":{"frontmatter":{"title":"(번역) React에서 UI와 로직 분리하기:헤드리스 컴포넌트를 사용한 클린 코드 접근법","categories":"translate react"},"fields":{"slug":"/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components/"}}},{"node":{"frontmatter":{"title":"(번역) 우리들을 위한 디자인 패턴","categories":"translate cs"},"fields":{"slug":"/cs/design-patterns-for-humans/"}}},{"node":{"frontmatter":{"title":"자바스크립트 가비지 컬렉션 알고리즘","categories":"javascript"},"fields":{"slug":"/javascript/garbage-collection/"}}},{"node":{"frontmatter":{"title":"(번역) React Query를 사용하여 서버 상태를 관리하는 방법","categories":"translate react react-query"},"fields":{"slug":"/react/How-to-manage-server-state-with-React-Query/"}}},{"node":{"frontmatter":{"title":"(번역) 캐시 시스템 설계할 때 기억해야 할 6가지 캐싱 전략","categories":"translate cs"},"fields":{"slug":"/cs/6-caching-strategies/"}}},{"node":{"frontmatter":{"title":"Proxy, Reflect와 메타프로그래밍","categories":"javascript"},"fields":{"slug":"/javascript/metaprogramming/"}}},{"node":{"frontmatter":{"title":"(번역) StructuredClone API를 사용하여 객체를 깊은 복사하는 법","categories":"translate javascript"},"fields":{"slug":"/javascript/deep-copying-objects-with-the-structuredclone-api/"}}},{"node":{"frontmatter":{"title":"날짜 입력 input 인터렉션 개발기","categories":"feature"},"fields":{"slug":"/feature/input-date/"}}},{"node":{"frontmatter":{"title":"Recoil 도입기(feat. 폴더구조)","categories":"react"},"fields":{"slug":"/react/recoil-introduction/"}}},{"node":{"frontmatter":{"title":"React Query useMutation에서 variable 옵셔널하게 사용하기","categories":"react react-query troubleshooting"},"fields":{"slug":"/trouble-shooting/how-to-use-variable-optional-in-useMutation/"}}},{"node":{"frontmatter":{"title":"React Query 시작하기 (feat. Tanstack)","categories":"react react-query"},"fields":{"slug":"/react/react-query-basic/"}}},{"node":{"frontmatter":{"title":"React의 상태관리 종류 4가지","categories":"react"},"fields":{"slug":"/react/react-state-management/"}}},{"node":{"frontmatter":{"title":"에러 핸들링에 대한 고민 (feat. React)","categories":"react"},"fields":{"slug":"/react/error-handle/"}}},{"node":{"frontmatter":{"title":"Touch, Mouse 이벤트 함께 다루기 (feat. dropdown)","categories":"feature browser"},"fields":{"slug":"/browser/touch-mouse-event/"}}},{"node":{"frontmatter":{"title":"Bitbucket과 Jenkins를 연동하여 테스트 자동화 (feat. Generic Webhook Trigger)","categories":"infra"},"fields":{"slug":"/infra/jenkins-bitbucket/"}}},{"node":{"frontmatter":{"title":"Promise를 사용하여 window.confirm 구현하기","categories":"feature"},"fields":{"slug":"/feature/confirm/"}}},{"node":{"frontmatter":{"title":"자바스크립트 테스트 개념정리","categories":"test"},"fields":{"slug":"/test/test-introduction/"}}},{"node":{"frontmatter":{"title":"로딩 indicator 구현시 고려해야할 접근성","categories":"a11y"},"fields":{"slug":"/a11y/loader/"}}},{"node":{"frontmatter":{"title":"모달 구현시 고려해야할 접근성","categories":"a11y"},"fields":{"slug":"/a11y/modal/"}}},{"node":{"frontmatter":{"title":"cypress 주요 api 모음","categories":"test"},"fields":{"slug":"/test/cypress-api/"}}},{"node":{"frontmatter":{"title":"postmessage를 이용하여 window 간에 통신하기","categories":"browser"},"fields":{"slug":"/browser/post-message/"}}},{"node":{"frontmatter":{"title":"api 서비스 설계","categories":"feature"},"fields":{"slug":"/feature/api-service/"}}},{"node":{"frontmatter":{"title":"Internet Explorer 이슈 모음","categories":"browser"},"fields":{"slug":"/browser/ie-issues/"}}},{"node":{"frontmatter":{"title":"PC용 브라우저(IE, Safari, Chrome, ...) 이슈 모음","categories":"browser"},"fields":{"slug":"/browser/pc-issues/"}}},{"node":{"frontmatter":{"title":"Gatsby 테마로 GitHub Blog 만들기","categories":"블로그"},"fields":{"slug":"/gatsby-github-blog/"}}},{"node":{"frontmatter":{"title":"쉽고 빠르게 나만의 개츠비(Gatsby) 블로그 만들기","categories":"블로그"},"fields":{"slug":"/gatsby-starter-zoomkoding-introduction/"}}}]}}} \ No newline at end of file diff --git a/posts/All/index.html b/posts/All/index.html index 93c9eb30..20851c3e 100644 --- a/posts/All/index.html +++ b/posts/All/index.html @@ -66,4 +66,4 @@ } } }) - Posts
\ No newline at end of file + Posts
\ No newline at end of file diff --git a/posts/a11y/index.html b/posts/a11y/index.html index 627e7dbb..6fb7928c 100644 --- a/posts/a11y/index.html +++ b/posts/a11y/index.html @@ -66,4 +66,4 @@ } } }) - Posts
\ No newline at end of file + Posts
\ No newline at end of file diff --git a/posts/browser/index.html b/posts/browser/index.html index 17cbc944..91f31acd 100644 --- a/posts/browser/index.html +++ b/posts/browser/index.html @@ -66,4 +66,4 @@ } } }) - Posts
브라우저 위치 및 크기 관련 API들

프론트 개발을 하다보면 웹 페이지의 다양한 요소들을 직접 제어해야 하는 경우가 있습니다. 이 글에서는 CSSOM의 네 가지 주요 좌표 체계인 Offset, Page, Viewport, Screen에 대해 자세히 알아보고, 위치와 크기 관련 API는 어떤것이 있는지 살펴볼 예정입니다. 이 글을 통해 웹 페이지 내에서 요소의 위치와 크기를 정밀하게 제어하는 데 도움이 되길 바랍니다. 표준 CSSOM 좌표 체계 보통 클라이언트 개발에서 좌표의 원점은 왼쪽 하단이 아닌, 왼쪽 상단 입니다. CSS 객체 모델에서는 Offset, Page, Viewport, Screen 네 가지 표준 좌표 체계가 있습니다. 출처: MDN Coordinate_systems Offset 정의: 오프셋 좌표 체계는 특정 요소의 상대적 위치를 나타내는 데 사용됩니다. 이는 요소의 왼쪽 상단 모서리를 시작점(원점)으로 합니다. 특징: 이 좌표 체계는 요소의 부모 요소를 기준으로 한 상대적 위치를 제공합니다. 마…

March 03, 2024
browser
Touch, Mouse 이벤트 함께 다루기 (feat. dropdown)

문제상황 dropdown 컴포넌트를 만들었는데, focus가 다른곳으로 이동하면 펼쳐져있던 dropdown이 접혀져야 해서 blur 이벤트 발생시 접히도록 했다. 그랬더니 펼쳐져 있는 항목 중 하나를 click 했을때, 위에서 설정한 blur 이벤트가 먼저 발생하면서 click 이벤트가 호출되지 않는 이슈가 문제였다. dropdown_비정상동작.gif 문제의 코드 TL;DR Touch, mouse 이벤트 순서 touchstart touchmove touchend mouseover mousemove mousedown mouseup click 해결방법 click시 처리해줘야 하는 이벤트 핸들러를 click 이 아닌 mousedown 에서 처리해준다. dropdown_정상동작.gif Dropdown CodeSandbox Touch and mouse 이벤트 그러면 지금부터는 web.dev 문서를 살펴보면서 알게된 touch 이벤트의 도입 배경과 touch, mouse 이벤트를 함께 다루기…

September 18, 2022
feature
browser
\ No newline at end of file + Posts
브라우저 위치 및 크기 관련 API들

프론트 개발을 하다보면 웹 페이지의 다양한 요소들을 직접 제어해야 하는 경우가 있습니다. 이 글에서는 CSSOM의 네 가지 주요 좌표 체계인 Offset, Page, Viewport, Screen에 대해 자세히 알아보고, 위치와 크기 관련 API는 어떤것이 있는지 살펴볼 예정입니다. 이 글을 통해 웹 페이지 내에서 요소의 위치와 크기를 정밀하게 제어하는 데 도움이 되길 바랍니다. 표준 CSSOM 좌표 체계 보통 클라이언트 개발에서 좌표의 원점은 왼쪽 하단이 아닌, 왼쪽 상단 입니다. CSS 객체 모델에서는 Offset, Page, Viewport, Screen 네 가지 표준 좌표 체계가 있습니다. 출처: MDN Coordinate_systems Offset 정의: 오프셋 좌표 체계는 특정 요소의 상대적 위치를 나타내는 데 사용됩니다. 이는 요소의 왼쪽 상단 모서리를 시작점(원점)으로 합니다. 특징: 이 좌표 체계는 요소의 부모 요소를 기준으로 한 상대적 위치를 제공합니다. 마…

March 03, 2024
browser
Touch, Mouse 이벤트 함께 다루기 (feat. dropdown)

문제상황 dropdown 컴포넌트를 만들었는데, focus가 다른곳으로 이동하면 펼쳐져있던 dropdown이 접혀져야 해서 blur 이벤트 발생시 접히도록 했다. 그랬더니 펼쳐져 있는 항목 중 하나를 click 했을때, 위에서 설정한 blur 이벤트가 먼저 발생하면서 click 이벤트가 호출되지 않는 이슈가 문제였다. dropdown_비정상동작.gif 문제의 코드 TL;DR Touch, mouse 이벤트 순서 touchstart touchmove touchend mouseover mousemove mousedown mouseup click 해결방법 click시 처리해줘야 하는 이벤트 핸들러를 click 이 아닌 mousedown 에서 처리해준다. dropdown_정상동작.gif Dropdown CodeSandbox Touch and mouse 이벤트 그러면 지금부터는 web.dev 문서를 살펴보면서 알게된 touch 이벤트의 도입 배경과 touch, mouse 이벤트를 함께 다루기…

September 18, 2022
feature
browser
\ No newline at end of file diff --git a/posts/cs/index.html b/posts/cs/index.html index 436d8e51..3b6eda0a 100644 --- a/posts/cs/index.html +++ b/posts/cs/index.html @@ -66,4 +66,4 @@ } } }) - Posts
(번역) 우리들을 위한 디자인 패턴

원글: https://github.com/kamranahmedse/design-patterns-for-humans/blob/master/readme.md 역자주: 이 글은 Kamran Ahmed의 글이며, PHP-7 기준으로 작성된 예제 코드를 타입스크립트로 변경하였습니다. logo 🎉 디자인 패턴에 대한 초간단 설명입니다! 🎉 누구든지 혼란스럽게 만들 수 있는 주제입니다. 이 글에서는 최대한 간단한 방식으로 설명함으로써 당신의 기억 속 (그리고 아마도 제 기억)에 각인시키기 위해 노력할 것입니다. 저의 다른 프로젝트도 확인하고 트위터에서 “안녕”이라고 인사해 보세요. 생성 패턴 구조 패턴 행동 패턴 심플 팩토리(Simple Factory) 어댑터(Adapter) 책임 연쇄(Chain of Responsibility) 팩토리 메서드(Factory Method) 브릿지(Bridge) 명령(Command) 추상 팩토리(Abstract Factory) 컴포지트(Composite) 반복자…

August 27, 2023
translate
cs
(번역) 캐시 시스템 설계할 때 기억해야 할 6가지 캐싱 전략

원글: https://javascript.plainenglish.io/6-caching-strategies-to-remember-while-designing-cache-system-da058a3757cf 캐시 시스템 관련 용어, 읽기 vs 쓰기 중심의 애플리케이션에서 캐싱 전략, 캐시를 무효화하는 방법 등에 대해서 알려드리겠습니다. 개요 캐싱의 목표는 원본 소스에서 데이터를 가져오는 횟수를 줄여 처리 속도를 높이고 대기 시간을 감소시키는 것입니다. 캐싱은 인메모리 캐싱, 디스크 캐싱, 데이터베이스 캐싱, CDN 캐싱과 같은 다양한 수준의 아키텍처에서 구현될 수 있습니다. 데이터는 각각 고유한 이점이 있는 다양한 기술을 사용하여 캐싱할 수 있습니다. 인메모리 캐싱은 컴퓨터의 주 메모리에 데이터를 저장하여 디스크 저장소 보다 빠른 액세스를 제공합니다. 반면 디스크 캐싱은 하드 디스크에 데이터를 저장하므로 주 메모리보다는 느리지만 원격 소스에서 데이터를 가져오는 것보다 빠릅니다. 데이터…

June 13, 2023
translate
cs
\ No newline at end of file + Posts
(번역) 우리들을 위한 디자인 패턴

원글: https://github.com/kamranahmedse/design-patterns-for-humans/blob/master/readme.md 역자주: 이 글은 Kamran Ahmed의 글이며, PHP-7 기준으로 작성된 예제 코드를 타입스크립트로 변경하였습니다. logo 🎉 디자인 패턴에 대한 초간단 설명입니다! 🎉 누구든지 혼란스럽게 만들 수 있는 주제입니다. 이 글에서는 최대한 간단한 방식으로 설명함으로써 당신의 기억 속 (그리고 아마도 제 기억)에 각인시키기 위해 노력할 것입니다. 저의 다른 프로젝트도 확인하고 트위터에서 “안녕”이라고 인사해 보세요. 생성 패턴 구조 패턴 행동 패턴 심플 팩토리(Simple Factory) 어댑터(Adapter) 책임 연쇄(Chain of Responsibility) 팩토리 메서드(Factory Method) 브릿지(Bridge) 명령(Command) 추상 팩토리(Abstract Factory) 컴포지트(Composite) 반복자…

August 27, 2023
translate
cs
(번역) 캐시 시스템 설계할 때 기억해야 할 6가지 캐싱 전략

원글: https://javascript.plainenglish.io/6-caching-strategies-to-remember-while-designing-cache-system-da058a3757cf 캐시 시스템 관련 용어, 읽기 vs 쓰기 중심의 애플리케이션에서 캐싱 전략, 캐시를 무효화하는 방법 등에 대해서 알려드리겠습니다. 개요 캐싱의 목표는 원본 소스에서 데이터를 가져오는 횟수를 줄여 처리 속도를 높이고 대기 시간을 감소시키는 것입니다. 캐싱은 인메모리 캐싱, 디스크 캐싱, 데이터베이스 캐싱, CDN 캐싱과 같은 다양한 수준의 아키텍처에서 구현될 수 있습니다. 데이터는 각각 고유한 이점이 있는 다양한 기술을 사용하여 캐싱할 수 있습니다. 인메모리 캐싱은 컴퓨터의 주 메모리에 데이터를 저장하여 디스크 저장소 보다 빠른 액세스를 제공합니다. 반면 디스크 캐싱은 하드 디스크에 데이터를 저장하므로 주 메모리보다는 느리지만 원격 소스에서 데이터를 가져오는 것보다 빠릅니다. 데이터…

June 13, 2023
translate
cs
\ No newline at end of file diff --git a/posts/feature/index.html b/posts/feature/index.html index 48630816..29859e10 100644 --- a/posts/feature/index.html +++ b/posts/feature/index.html @@ -66,4 +66,4 @@ } } }) - Posts
날짜 입력 input 인터렉션 개발기

Date와 인연이 깊은지 날짜 선택 UI를 custom 해서 개발해야 하는 경험을 두 번째 하게 되었다. mui datepicker를 붙이기엔 너무 과해서 react-calendar 라이브러리를 사용하려고 했는데, 문제는 input은 함께 딸려오지 않기 때문에 input 관련 interaction은 직접 구현해야 했다. 인 하우스용 서비스라 여러 라이브러리 참고해서 직접 인터렉션을 정의하고 구현해 보았는데 기록 차 경험을 남겨본다! TL;DR; 전체 구현 결과물은 code sandbox에서 확인할 수 있다. DatePicker 컴포넌트를 개발해 보자! (calendar는 라이브러리를 사용) 딱히 input에 대한 큰 요구사항이 없이 기능 개발을 해야 하는 경우 그냥 react-date-picker를 써라 DatePicker 컴포넌트 개발 dependency react-calendar dayjs props 설계 minDate, maxDate: date range를 지원해야하므로 …

April 23, 2023
feature
Touch, Mouse 이벤트 함께 다루기 (feat. dropdown)

문제상황 dropdown 컴포넌트를 만들었는데, focus가 다른곳으로 이동하면 펼쳐져있던 dropdown이 접혀져야 해서 blur 이벤트 발생시 접히도록 했다. 그랬더니 펼쳐져 있는 항목 중 하나를 click 했을때, 위에서 설정한 blur 이벤트가 먼저 발생하면서 click 이벤트가 호출되지 않는 이슈가 문제였다. dropdown_비정상동작.gif 문제의 코드 TL;DR Touch, mouse 이벤트 순서 touchstart touchmove touchend mouseover mousemove mousedown mouseup click 해결방법 click시 처리해줘야 하는 이벤트 핸들러를 click 이 아닌 mousedown 에서 처리해준다. dropdown_정상동작.gif Dropdown CodeSandbox Touch and mouse 이벤트 그러면 지금부터는 web.dev 문서를 살펴보면서 알게된 touch 이벤트의 도입 배경과 touch, mouse 이벤트를 함께 다루기…

September 18, 2022
feature
browser
Promise를 사용하여 window.confirm 구현하기

보통 어플리케이션에서 공통된 디자인의 confirm 창을 사용한다. 이때, window.confirm과 같이 고객의 OK/CANCEL 클릭 여부에 따라서 다음 동작을 이어나가게 하고싶다면 어떻게 구현해야할까? 준비물 Modal Component (global 하게 등록) Global State Modal Hook 보통 웹 어플리케이션에서는 공통된 디자인의 confirm을 사용하므로 화면에 보여지는 역할을 하는 Modal Component가 필요하다. 이 컴포넌트는 어플리케이션 전반에 걸쳐서 자주 사용하는 라이브러리이므로 App.js에서 global 하게 로드한다. global 하게 등록된 Modal 컴포넌트를 제어하기 위해서는 어플리케이션 어느 곳에서든 제어 가능하게 하기 위해서 global state가 필요하다. 그래서 modal 관련 state가 변경되었을때, Modal 컴포넌트가 render 되게 한다. 이제 window.confirm 처럼 사용하기 위해 confirm 함수를…

August 19, 2022
feature
\ No newline at end of file + Posts
날짜 입력 input 인터렉션 개발기

Date와 인연이 깊은지 날짜 선택 UI를 custom 해서 개발해야 하는 경험을 두 번째 하게 되었다. mui datepicker를 붙이기엔 너무 과해서 react-calendar 라이브러리를 사용하려고 했는데, 문제는 input은 함께 딸려오지 않기 때문에 input 관련 interaction은 직접 구현해야 했다. 인 하우스용 서비스라 여러 라이브러리 참고해서 직접 인터렉션을 정의하고 구현해 보았는데 기록 차 경험을 남겨본다! TL;DR; 전체 구현 결과물은 code sandbox에서 확인할 수 있다. DatePicker 컴포넌트를 개발해 보자! (calendar는 라이브러리를 사용) 딱히 input에 대한 큰 요구사항이 없이 기능 개발을 해야 하는 경우 그냥 react-date-picker를 써라 DatePicker 컴포넌트 개발 dependency react-calendar dayjs props 설계 minDate, maxDate: date range를 지원해야하므로 …

April 23, 2023
feature
Touch, Mouse 이벤트 함께 다루기 (feat. dropdown)

문제상황 dropdown 컴포넌트를 만들었는데, focus가 다른곳으로 이동하면 펼쳐져있던 dropdown이 접혀져야 해서 blur 이벤트 발생시 접히도록 했다. 그랬더니 펼쳐져 있는 항목 중 하나를 click 했을때, 위에서 설정한 blur 이벤트가 먼저 발생하면서 click 이벤트가 호출되지 않는 이슈가 문제였다. dropdown_비정상동작.gif 문제의 코드 TL;DR Touch, mouse 이벤트 순서 touchstart touchmove touchend mouseover mousemove mousedown mouseup click 해결방법 click시 처리해줘야 하는 이벤트 핸들러를 click 이 아닌 mousedown 에서 처리해준다. dropdown_정상동작.gif Dropdown CodeSandbox Touch and mouse 이벤트 그러면 지금부터는 web.dev 문서를 살펴보면서 알게된 touch 이벤트의 도입 배경과 touch, mouse 이벤트를 함께 다루기…

September 18, 2022
feature
browser
Promise를 사용하여 window.confirm 구현하기

보통 어플리케이션에서 공통된 디자인의 confirm 창을 사용한다. 이때, window.confirm과 같이 고객의 OK/CANCEL 클릭 여부에 따라서 다음 동작을 이어나가게 하고싶다면 어떻게 구현해야할까? 준비물 Modal Component (global 하게 등록) Global State Modal Hook 보통 웹 어플리케이션에서는 공통된 디자인의 confirm을 사용하므로 화면에 보여지는 역할을 하는 Modal Component가 필요하다. 이 컴포넌트는 어플리케이션 전반에 걸쳐서 자주 사용하는 라이브러리이므로 App.js에서 global 하게 로드한다. global 하게 등록된 Modal 컴포넌트를 제어하기 위해서는 어플리케이션 어느 곳에서든 제어 가능하게 하기 위해서 global state가 필요하다. 그래서 modal 관련 state가 변경되었을때, Modal 컴포넌트가 render 되게 한다. 이제 window.confirm 처럼 사용하기 위해 confirm 함수를…

August 19, 2022
feature
\ No newline at end of file diff --git a/posts/framework/index.html b/posts/framework/index.html index 25fdce29..dda2f718 100644 --- a/posts/framework/index.html +++ b/posts/framework/index.html @@ -66,4 +66,4 @@ } } }) - Posts
‘의도적으로 무시’하는 코드에 대한 TypeScript와 ESLint의 다른 처리 방식

TL;DR; TypeScript와 ESLint는 “의도적으로 무시”하는 코드에 대해 다른 접근 방식을 가집니다. TypeScript는 언더스코어(_) 접두사로 시작하는 변수를 자동으로 무시하지만, ESLint의 no-unused-vars 규칙은 기본적으로 더 엄격하므로, 언더스코어로 시작하는 변수도 사용되지 않은 것으로 간주하여 오류를 발생시킵니다. 개발자는 ESLint 설정을 통해 “의도적으로 무시”하는 코드에 대해 TypeScript와 유사한 동작을 구현하거나 더 세밀한 제어를 할 수 있습니다. “의도적으로 무시”하는 코드 아래 예시와 같은 코드를 작성해본 경험이 있으신가요? 우리는 가끔 “의도적으로 무시” 하는 것으로 간주하는 코드를 작성해야 할 때가 있습니다. 루프 인덱스 무시: _index는 사용되지 않지만, entries() 메서드의 반환 값을 구조 분해하기 위해 필요 콜백 함수에서 매개변수 무시: _item은 사용되지 않지만, index에 접근하기 위해 선언 구조 분…

October 26, 2024
framework
Astro로 알아보는 Selective & Progressive Hydration

Astro는 애플리케이션을 구축이 아닌 콘텐츠에 중점을 두고 설계된 정적 사이트 빌더입니다. 따라서 블로그, 포트폴리오, 이벤트용 마케팅 성격의 사이트 개발에 매우 적합한 프레임워크 입니다. Astro 컴포넌트에 대해 먼저 간략히 알아보고, 어떻게 여러 프레임워크(React, Preact,Svelte, Vue, SolidJS)를 하나의 프로젝트에서 혼합해서 사용할 수 있는지 알아보도록 하겠습니다. Astro 컴포넌트 Astro 컴포넌트는 모든 Astro 프로젝트의 기본 구성 요소입니다. 이는 클라이언트 측 런타임이 없는 HTML 전용 템플릿 컴포넌트로 .astro 파일 확장자 사용합니다. Astro 컴포넌트에 대해 알아야 할 가장 중요한 점은 클라이언트에서 렌더링되지 않는다는 것입니다. HTML로 렌더링되며, 컴포넌트 스크립트는 브라우저로 전송되는 최종 페이지에서 모두 제거됩니다. 구성(스크립트 + 템플릿) .astro 파일은 스크립트와 템플릿으로 구성됩니다. src/compone…

October 13, 2024
framework
\ No newline at end of file + Posts
‘의도적으로 무시’하는 코드에 대한 TypeScript와 ESLint의 다른 처리 방식

TL;DR; TypeScript와 ESLint는 “의도적으로 무시”하는 코드에 대해 다른 접근 방식을 가집니다. TypeScript는 언더스코어(_) 접두사로 시작하는 변수를 자동으로 무시하지만, ESLint의 no-unused-vars 규칙은 기본적으로 더 엄격하므로, 언더스코어로 시작하는 변수도 사용되지 않은 것으로 간주하여 오류를 발생시킵니다. 개발자는 ESLint 설정을 통해 “의도적으로 무시”하는 코드에 대해 TypeScript와 유사한 동작을 구현하거나 더 세밀한 제어를 할 수 있습니다. “의도적으로 무시”하는 코드 아래 예시와 같은 코드를 작성해본 경험이 있으신가요? 우리는 가끔 “의도적으로 무시” 하는 것으로 간주하는 코드를 작성해야 할 때가 있습니다. 루프 인덱스 무시: _index는 사용되지 않지만, entries() 메서드의 반환 값을 구조 분해하기 위해 필요 콜백 함수에서 매개변수 무시: _item은 사용되지 않지만, index에 접근하기 위해 선언 구조 분…

October 26, 2024
framework
Astro로 알아보는 Selective & Progressive Hydration

Astro는 애플리케이션을 구축이 아닌 콘텐츠에 중점을 두고 설계된 정적 사이트 빌더입니다. 따라서 블로그, 포트폴리오, 이벤트용 마케팅 성격의 사이트 개발에 매우 적합한 프레임워크 입니다. Astro 컴포넌트에 대해 먼저 간략히 알아보고, 어떻게 여러 프레임워크(React, Preact,Svelte, Vue, SolidJS)를 하나의 프로젝트에서 혼합해서 사용할 수 있는지 알아보도록 하겠습니다. Astro 컴포넌트 Astro 컴포넌트는 모든 Astro 프로젝트의 기본 구성 요소입니다. 이는 클라이언트 측 런타임이 없는 HTML 전용 템플릿 컴포넌트로 .astro 파일 확장자 사용합니다. Astro 컴포넌트에 대해 알아야 할 가장 중요한 점은 클라이언트에서 렌더링되지 않는다는 것입니다. HTML로 렌더링되며, 컴포넌트 스크립트는 브라우저로 전송되는 최종 페이지에서 모두 제거됩니다. 구성(스크립트 + 템플릿) .astro 파일은 스크립트와 템플릿으로 구성됩니다. src/compone…

October 13, 2024
framework
\ No newline at end of file diff --git a/posts/index.html b/posts/index.html index 70e87af4..4ce558c3 100644 --- a/posts/index.html +++ b/posts/index.html @@ -66,7 +66,7 @@ } } }) - Posts
‘의도적으로 무시’하는 코드에 대한 TypeScript와 ESLint의 다른 처리 방식

TL;DR; TypeScript와 ESLint는 “의도적으로 무시”하는 코드에 대해 다른 접근 방식을 가집니다. TypeScript는 언더스코어(_) 접두사로 시작하는 변수를 자동으로 무시하지만, ESLint의 no-unused-vars 규칙은 기본적으로 더 엄격하므로, 언더스코어로 시작하는 변수도 사용되지 않은 것으로 간주하여 오류를 발생시킵니다. 개발자는 ESLint 설정을 통해 “의도적으로 무시”하는 코드에 대해 TypeScript와 유사한 동작을 구현하거나 더 세밀한 제어를 할 수 있습니다. “의도적으로 무시”하는 코드 아래 예시와 같은 코드를 작성해본 경험이 있으신가요? 우리는 가끔 “의도적으로 무시” 하는 것으로 간주하는 코드를 작성해야 할 때가 있습니다. 루프 인덱스 무시: _index는 사용되지 않지만, entries() 메서드의 반환 값을 구조 분해하기 위해 필요 콜백 함수에서 매개변수 무시: _item은 사용되지 않지만, index에 접근하기 위해 선언 구조 분…

October 26, 2024
framework
Astro로 알아보는 Selective & Progressive Hydration

Astro는 애플리케이션을 구축이 아닌 콘텐츠에 중점을 두고 설계된 정적 사이트 빌더입니다. 따라서 블로그, 포트폴리오, 이벤트용 마케팅 성격의 사이트 개발에 매우 적합한 프레임워크 입니다. Astro 컴포넌트에 대해 먼저 간략히 알아보고, 어떻게 여러 프레임워크(React, Preact,Svelte, Vue, SolidJS)를 하나의 프로젝트에서 혼합해서 사용할 수 있는지 알아보도록 하겠습니다. Astro 컴포넌트 Astro 컴포넌트는 모든 Astro 프로젝트의 기본 구성 요소입니다. 이는 클라이언트 측 런타임이 없는 HTML 전용 템플릿 컴포넌트로 .astro 파일 확장자 사용합니다. Astro 컴포넌트에 대해 알아야 할 가장 중요한 점은 클라이언트에서 렌더링되지 않는다는 것입니다. HTML로 렌더링되며, 컴포넌트 스크립트는 브라우저로 전송되는 최종 페이지에서 모두 제거됩니다. 구성(스크립트 + 템플릿) .astro 파일은 스크립트와 템플릿으로 구성됩니다. src/compone…

October 13, 2024
framework
환경 변수(process.env)를 구조 분해 할당하면 안되는 이유

process.env 값을 구조 분해 할당 하셨나요? 우리가 환경변수를 접근할 때 객체의 속성값에 접근할 때 사용하는 점 표기법을 사용하기 때문에 구조 분해 할당을 자연스럽게 사용해도 될 것 같은데요. 이런 경우, 왜 에러가 발생하는지 이번 글을 통해 알아보겠습니다. 문제 상황 다음과 같은 코드가 있다고 가정해 봅시다. 위 코드는 process.env 객체에서 API_KEY와 NEXT_PUBLIC_ANALYTICS_ID 변수를 구조 분해 할당하여 사용하려고 합니다. 그러나 Next.js에서는 이 코드가 제대로 작동하지 않을 수 있고, 오류가 발생할 수 있습니다. 왜 이런 문제가 발생할까요? process.env는 일반적인 자바스크립트 객체와는 다르기 때문입니다. 이는 Node.js 환경에서 환경 변수를 담고 있는 특수한 객체로, 모든 속성이 문자열 형태로 저장됩니다. Next.js는 빌드 타임과 런타임에서 환경 변수에 접근하는 방식이 다르므로, 이로 인해 구조 분해 할당을 사용할 …

June 26, 2024
react
next
(번역) 타입스크립트에서 'As Const' 이해하기

원글: https://www.omarileon.me/blog/typescript-as-const 새로운 구문인 “상수 어설션(const assertions)“은 TypeScript 3.4에서 도입되었습니다. 이것은 변수가 변경되지 않을 것(immutable)이고 가능한 엄격한 타입을 제공해야 한다는 것을 타입스크립트에 알려줍니다. 이는 타입의 종류에 따라 다르게 영향을 미치므로, 이 글에서는 상수 어설션을 어떻게 사용하고 왜 유용한지에 대해 설명할 것입니다. 문자열/숫자 문자열이나 숫자에 “as const”를 추가하면 특정 값으로 타입을 좁힐 수 있습니다. 숫자의 경우 문자열 또는 숫자의 경우 일반적으로 “const”를 사용하여 변수를 정의하면 동일한 효과를 얻을 수 있기 때문에 덜 유용합니다. 런타임 안전성이라는 추가 이점도 있습니다. 때로는 변수로 값을 정의하지 않고, 단순히 문자열 리터럴을 사용하고 싶을 수 있습니다. 예를 들어 값을 반환할 때처럼요. 그때 “as const”…

April 14, 2024
translate
typescript
타입스크립트 타입 호환성 문제 해결하기 "as const vs satisfies"

Argument of type ‘string’ is not assignable to parameter of type ‘“apple” | “banana”‘.(2345) 타입스크립트 사용시 때때로 예상치 못한 타입 호환성 문제에 직면하곤 합니다. 엄격한 타입 시스템을 가지고 있는 타입스크립트로 코드를 작성하다 보면 더욱 이러한 문제와 자주 직면합니다. 하지만 다행히도, 타입스크립트는 이러한 문제들을 해결할 수 있는 강력한 도구들을 제공합니다. 이 글에서는 타입 호환성 문제를 해결하는 데 있어 as const와 satisfies라는 두 가지 도구의 사용 방법과 각각의 장점에 대해 알아보겠습니다. 먼저 우리가 자주 마주할 수 있는 상황을 예시 코드로 살펴보겠습니다. 위에 에러 메시지는 함수의 인자로 예상되는 타입과 실제로 전달된 타입이 일치하지 않을 때 발생합니다. 이 경우, printPerson 함수는 "apple" 또는 "banana"라는 구체적인 문자열 타입을 기대하지만, perso…

March 31, 2024
typescript
(번역) 스트리밍 HTML과 DOM 비교 알고리즘

원글: https://aralroca.com/blog/html-node-streaming 썸네일 최근 몇 년간 브라우저는 HTML과 자바스크립트를 스트리밍을 지원하기 시작했습니다. 이 글에서는 이에 대한 장점과 브라우저가 자동으로 수행하지 않는 다른 작업들을 통해 스트리밍의 이점을 최대한 활용할 수 있는 방법에 대해 이야기할 것입니다. 스트리밍 HTML 초기 로드하는 동안에는 브라우저가 자동으로 처리하기 때문에 크게 신경 쓸 필요가 없습니다. 스트리밍 하는 동안 HTML 청크를 받으면, 브라우저는 그 콘텐츠를 출력합니다. 서버에서 스트리밍을 활성화하려면 헤더를 조정해야 합니다. 아래는 예시입니다. 그리고 응답에서는 ReadableStream을 사용합니다. 이것은 Bun을 사용한 예시입니다. enqueue 안에 있는 각 문자열은 브라우저가 받게 될 청크입니다. 스트리밍 중 HTML 콘텐츠 변경 성능상의 이점이 많기 때문에 많이 사용되는 방법 중 하나는 스트리밍 되는 동안 HTML 콘…

March 21, 2024
translate
react
브라우저 위치 및 크기 관련 API들

프론트 개발을 하다보면 웹 페이지의 다양한 요소들을 직접 제어해야 하는 경우가 있습니다. 이 글에서는 CSSOM의 네 가지 주요 좌표 체계인 Offset, Page, Viewport, Screen에 대해 자세히 알아보고, 위치와 크기 관련 API는 어떤것이 있는지 살펴볼 예정입니다. 이 글을 통해 웹 페이지 내에서 요소의 위치와 크기를 정밀하게 제어하는 데 도움이 되길 바랍니다. 표준 CSSOM 좌표 체계 보통 클라이언트 개발에서 좌표의 원점은 왼쪽 하단이 아닌, 왼쪽 상단 입니다. CSS 객체 모델에서는 Offset, Page, Viewport, Screen 네 가지 표준 좌표 체계가 있습니다. 출처: MDN Coordinate_systems Offset 정의: 오프셋 좌표 체계는 특정 요소의 상대적 위치를 나타내는 데 사용됩니다. 이는 요소의 왼쪽 상단 모서리를 시작점(원점)으로 합니다. 특징: 이 좌표 체계는 요소의 부모 요소를 기준으로 한 상대적 위치를 제공합니다. 마…

March 03, 2024
browser
(번역) 접근성을 고려하여 CSS 작성하기

원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939 CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다. 이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다. 읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다. 약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리…

February 18, 2024
translate
a11y
docker에서 자주쓰는 명령어

Docker 엔진 시작하기/종료하기 도커 엔진: 자동 실행 설정 활성화시, 컴퓨터를 켰을 때 함께 자동으로 실행 Docker Desktop은 도커 엔진 자동으로 실행하도록 설정 되어있음. 리눅스 명령어로 엔진 시작, 종료, 자동 실행 설정 방법 systemctrl: 서비스 시작 및 중지 관련 리눅스 명령어 (system control) 컨테이너: 실행시킬 스크립트가 따로 필요. 컴퓨터를 켰을 때 함께 자동으로 실행 되지 않음. 컨테이너의 기본적인 사용 방법 컨테이너를 다루는 모든 명령어는 docker 명령어로 시작 도커 명령어 구성: docker 커맨드(무엇을 어떻게) 대상 + 옵션, 인자: docker 커맨드(무엇을 어떻게) 옵션 대상 인자 상위 커맨드는 생략 가능하다?! ex) docker run, docker start 커맨드(무엇을 어떻게)는 상위커맨드와 하위커맨드로 나뉘어짐. container 상위 커맨들를 생략하는 경우가 있음. 도커 1.13부터 커맨드가 재편되면서 …

February 18, 2024
infra
(번역) 리액트 서버 컴포넌트를 위한 스토리북

원문: https://storybook.js.org/blog/storybook-react-server-components/ 스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용 리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다. 가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다. 이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 …

February 02, 2024
translate
react
Next.js app router에서 React Query 사용하면서 고민했던 것들

지난 글에서 react-query의 hydrate, dehydrate을 통해 서버에서 prefetching 한 데이터 사용하는 방법에 대해서 살펴보았습니다. 서버에서 prefetching 한 데이터 사용하기 오늘은 조금 실용적으로 Next.js 13, 14 버전의 app router에서 react-query를 어떻게 사용하고 세팅하면 좋을지 고민했던 내용에 대해서 이야기해보도록 하겠습니다. 해당 글은 23년 10월에 메이저 버전 업데이트된, Next.js 14와 React-Query 5 를 기준으로 작성된 글입니다. 예시로 보여주는 코드는 저의 next-14-react-query repo에서 확인 할 수 있습니다. 제가 크게 고민했던 문제는 아래 세 가지입니다. Hydrate vs ReactQueryStreamedHydration 두 가지 API 중에 어떤 것을 채택할까? Hydration API 사용 시에 RSC, RCC 모두에서 깔끔한 코드를 유지하려면 queryOption은 …

January 07, 2024
react
react-query
(번역) 2024년 자바스크립트의 5가지 가장 혁신적인 제안

원문: https://javascript.plainenglish.io/the-5-most-innovative-proposals-for-javascript-planned-for-2024-22139dd2f546 JavaScript Logo 2024 자바스크립트가 빠른 속도로 발전함에 따라 2024년은 이 프로그래밍 언어에 있어 중요한 해가 될 것입니다. 이 글에서는 개발자들이 자바스크립트 코딩에 접근하는 방식을 재정의하기 위한 최신 제안을 자세히 살펴보겠습니다. 이 글이 유용하길 바랍니다! 시작해 봅시다! 목차 데코레이터 Temporal API 파이프라인 연산자 Error cause 레코드와 튜플 1. 데코레이터 자바스크립트에 데코레이터가 도입된 것은 개발자가 클래스, 메서드, 프로퍼티 및 매개변수의 동작을 조작하고 개선할 수 있는 방법이 크게 발전했음을 의미합니다. 파이썬과 타입스크립트와 같은 다른 프로그래밍 언어에서 유래한 데코레이터는 기능을 추가하거나 수정하는 간결하고 선언적인…

December 20, 2023
translate
javascript
서버에서 React Query prefetching 한 데이터 사용하기

Next.js나 Remix 같은 프레임워크 내에서 React-Query를 사용한다면, 서버 렌더링 될 때 요청 후 응답받은 데이터를 SPA 방식으로 전환되고 나서도 유지할 수 있을까요? 어떻게 가능할까요? React Query의 hydrate와 dehydrate는 서버에서 미리 가져온 데이터를 클라이언트 사이드에서 재사용 할 수 있게 해줍니다. 이번 글을 통해 서버 렌더링과 어떻게 이를 가능하게 하는지 hydrate와 dehydrate에 대해서 알아봅시다. Server Rendering 서버 렌더링은 사용자가 페이지를 로드하는 즉시 볼 수 있는 초기 HTML을 서버에서 생성하는 행위입니다. 이는 페이지 요청 시 즉시 발생할 수 있으며(SSR), 이전 요청이 캐시 되었거나 빌드 시간에 미리 생성(SSG) 할 수도 있습니다. 클라이언트 렌더링 애플리케이션에서는 사용자에게 화면에 콘텐츠를 표시하기 전에 최소 3번의 서버 왕복(roundtrips)이 필요합니다. 서버 렌더링은 위의 과정…

December 10, 2023
react
react-query
(번역) 시그널(Signal)에 대한 소개

원문: https://preactjs.com/blog/introducing-signals/ 시그널은 앱이 복잡해져도 빠른 속도를 유지하도록 하는 상태 표현 방식입니다. 시그널은 반응형 원칙에 기반을 두고 있으며, 가상 돔에 최적화된 독특한 구현을 통해 개발자에게 훌륭한 경험을 제공합니다. 본질적으로 시그널은 특정 값을 가지고 있는 .value 속성을 가진 객체입니다. 컴포넌트 내에서 시그널의 value 속성에 접근하면, 그 시그널의 값이 변경될 때 해당 컴포넌트가 자동으로 업데이트됩니다. 이는 간단하고 작성하기 쉬울 뿐만 아니라, 앱이 얼마나 많은 컴포넌트를 가지고 있든 상태 업데이트가 빠르게 유지되도록 보장합니다. 시그널은 기본적으로 빠르며, 백그라운드에서 자동으로 업데이트를 최적화해줍니다. REPL에서 실행 시그널은 훅과 달리 컴포넌트 내부 또는 외부에서 사용할 수 있습니다. 또한 시그널은 훅과 클래스 컴포넌트 모두에서 훌륭하게 작동하므로, 기존 지식을 활용하며 자신의 속도에 …

November 27, 2023
translate
react
(번역) React에서 UI와 로직 분리하기:헤드리스 컴포넌트를 사용한 클린 코드 접근법

원글: https://itnext.io/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components-82e46b5820c 프런트엔드 개발 영역에서는 용어와 패러다임이 때로는 이해하기 어려울 수 있으며 ‘헤드리스 UI’ 또는 ‘헤드리스 컴포넌트’도 이 범주에 속할 수 있습니다. 이러한 용어들이 무엇을 의미하는지 궁금해서 고개를 갸웃거리고, 혼자만 그런 것이 아닙니다. 사실, 혼란스러운 이름에도 불구하고 이러한 개념들은 복잡한 사용자 인터페이스 관리를 상당히 단순화할 수 있는 매력적인 전략입니다. 헤드리스 컴포넌트는 난해해 보일 수 있지만, 그 진정한 힘은 유연성, 재사용 가능성, 그리고 코드베이스의 구성과 깔끔함을 향상시킬 수 있는 능력에 있습니다. 이 글에서는 이 패턴이 정확히 무엇인지, 왜 유용한지, 그리고 인터페이스 디자인에 대한 접근 방식을 어떻게 혁신할 수 있는지에 대해 탐구해 볼 것입니다. …

September 13, 2023
translate
react
(번역) 우리들을 위한 디자인 패턴

원글: https://github.com/kamranahmedse/design-patterns-for-humans/blob/master/readme.md 역자주: 이 글은 Kamran Ahmed의 글이며, PHP-7 기준으로 작성된 예제 코드를 타입스크립트로 변경하였습니다. logo 🎉 디자인 패턴에 대한 초간단 설명입니다! 🎉 누구든지 혼란스럽게 만들 수 있는 주제입니다. 이 글에서는 최대한 간단한 방식으로 설명함으로써 당신의 기억 속 (그리고 아마도 제 기억)에 각인시키기 위해 노력할 것입니다. 저의 다른 프로젝트도 확인하고 트위터에서 “안녕”이라고 인사해 보세요. 생성 패턴 구조 패턴 행동 패턴 심플 팩토리(Simple Factory) 어댑터(Adapter) 책임 연쇄(Chain of Responsibility) 팩토리 메서드(Factory Method) 브릿지(Bridge) 명령(Command) 추상 팩토리(Abstract Factory) 컴포지트(Composite) 반복자…

August 27, 2023
translate
cs
자바스크립트 가비지 컬렉션 알고리즘

힙과 스택 메모리의 차이를 살펴보고, 자바스크립트 대표 엔진인 V8의 Resident set 구조와 Minor GC, Major GC 의 동작 알고리즘에 대해서 자세하게 살펴 보도록 하겠습니다. 이 글이 가비지 컬렉터가 어떻게 동작하는지 이해하는데 도움이 되길 바랍니다. V8 메모리 구조 (feat. Resident set) Resident Set은 실행 중인 프로그램의 메모리 관리를 위해 사용되는 용어로, 현재 V8 프로세스가 사용하는 모든 메모리를 나타냅니다. 이것은 JavaScript 코드, 객체, 함수, 변수 등을 저장하는 데 필요한 모든 메모리를 포함합니다. Resident Set은 다음과 같이 여러 세그먼트로 구성됩니다. + Posts

‘의도적으로 무시’하는 코드에 대한 TypeScript와 ESLint의 다른 처리 방식

TL;DR; TypeScript와 ESLint는 “의도적으로 무시”하는 코드에 대해 다른 접근 방식을 가집니다. TypeScript는 언더스코어(_) 접두사로 시작하는 변수를 자동으로 무시하지만, ESLint의 no-unused-vars 규칙은 기본적으로 더 엄격하므로, 언더스코어로 시작하는 변수도 사용되지 않은 것으로 간주하여 오류를 발생시킵니다. 개발자는 ESLint 설정을 통해 “의도적으로 무시”하는 코드에 대해 TypeScript와 유사한 동작을 구현하거나 더 세밀한 제어를 할 수 있습니다. “의도적으로 무시”하는 코드 아래 예시와 같은 코드를 작성해본 경험이 있으신가요? 우리는 가끔 “의도적으로 무시” 하는 것으로 간주하는 코드를 작성해야 할 때가 있습니다. 루프 인덱스 무시: _index는 사용되지 않지만, entries() 메서드의 반환 값을 구조 분해하기 위해 필요 콜백 함수에서 매개변수 무시: _item은 사용되지 않지만, index에 접근하기 위해 선언 구조 분…

October 26, 2024
framework
Astro로 알아보는 Selective & Progressive Hydration

Astro는 애플리케이션을 구축이 아닌 콘텐츠에 중점을 두고 설계된 정적 사이트 빌더입니다. 따라서 블로그, 포트폴리오, 이벤트용 마케팅 성격의 사이트 개발에 매우 적합한 프레임워크 입니다. Astro 컴포넌트에 대해 먼저 간략히 알아보고, 어떻게 여러 프레임워크(React, Preact,Svelte, Vue, SolidJS)를 하나의 프로젝트에서 혼합해서 사용할 수 있는지 알아보도록 하겠습니다. Astro 컴포넌트 Astro 컴포넌트는 모든 Astro 프로젝트의 기본 구성 요소입니다. 이는 클라이언트 측 런타임이 없는 HTML 전용 템플릿 컴포넌트로 .astro 파일 확장자 사용합니다. Astro 컴포넌트에 대해 알아야 할 가장 중요한 점은 클라이언트에서 렌더링되지 않는다는 것입니다. HTML로 렌더링되며, 컴포넌트 스크립트는 브라우저로 전송되는 최종 페이지에서 모두 제거됩니다. 구성(스크립트 + 템플릿) .astro 파일은 스크립트와 템플릿으로 구성됩니다. src/compone…

October 13, 2024
framework
환경 변수(process.env)를 구조 분해 할당하면 안되는 이유

process.env 값을 구조 분해 할당 하셨나요? 우리가 환경변수를 접근할 때 객체의 속성값에 접근할 때 사용하는 점 표기법을 사용하기 때문에 구조 분해 할당을 자연스럽게 사용해도 될 것 같은데요. 이런 경우, 왜 에러가 발생하는지 이번 글을 통해 알아보겠습니다. 문제 상황 다음과 같은 코드가 있다고 가정해 봅시다. 위 코드는 process.env 객체에서 API_KEY와 NEXT_PUBLIC_ANALYTICS_ID 변수를 구조 분해 할당하여 사용하려고 합니다. 그러나 Next.js에서는 이 코드가 제대로 작동하지 않을 수 있고, 오류가 발생할 수 있습니다. 왜 이런 문제가 발생할까요? process.env는 일반적인 자바스크립트 객체와는 다르기 때문입니다. 이는 Node.js 환경에서 환경 변수를 담고 있는 특수한 객체로, 모든 속성이 문자열 형태로 저장됩니다. Next.js는 빌드 타임과 런타임에서 환경 변수에 접근하는 방식이 다르므로, 이로 인해 구조 분해 할당을 사용할 …

June 26, 2024
react
next
(번역) 타입스크립트에서 'As Const' 이해하기

원글: https://www.omarileon.me/blog/typescript-as-const 새로운 구문인 “상수 어설션(const assertions)“은 TypeScript 3.4에서 도입되었습니다. 이것은 변수가 변경되지 않을 것(immutable)이고 가능한 엄격한 타입을 제공해야 한다는 것을 타입스크립트에 알려줍니다. 이는 타입의 종류에 따라 다르게 영향을 미치므로, 이 글에서는 상수 어설션을 어떻게 사용하고 왜 유용한지에 대해 설명할 것입니다. 문자열/숫자 문자열이나 숫자에 “as const”를 추가하면 특정 값으로 타입을 좁힐 수 있습니다. 숫자의 경우 문자열 또는 숫자의 경우 일반적으로 “const”를 사용하여 변수를 정의하면 동일한 효과를 얻을 수 있기 때문에 덜 유용합니다. 런타임 안전성이라는 추가 이점도 있습니다. 때로는 변수로 값을 정의하지 않고, 단순히 문자열 리터럴을 사용하고 싶을 수 있습니다. 예를 들어 값을 반환할 때처럼요. 그때 “as const”…

April 14, 2024
translate
typescript
타입스크립트 타입 호환성 문제 해결하기 "as const vs satisfies"

Argument of type ‘string’ is not assignable to parameter of type ‘“apple” | “banana”‘.(2345) 타입스크립트 사용시 때때로 예상치 못한 타입 호환성 문제에 직면하곤 합니다. 엄격한 타입 시스템을 가지고 있는 타입스크립트로 코드를 작성하다 보면 더욱 이러한 문제와 자주 직면합니다. 하지만 다행히도, 타입스크립트는 이러한 문제들을 해결할 수 있는 강력한 도구들을 제공합니다. 이 글에서는 타입 호환성 문제를 해결하는 데 있어 as const와 satisfies라는 두 가지 도구의 사용 방법과 각각의 장점에 대해 알아보겠습니다. 먼저 우리가 자주 마주할 수 있는 상황을 예시 코드로 살펴보겠습니다. 위에 에러 메시지는 함수의 인자로 예상되는 타입과 실제로 전달된 타입이 일치하지 않을 때 발생합니다. 이 경우, printPerson 함수는 "apple" 또는 "banana"라는 구체적인 문자열 타입을 기대하지만, perso…

March 31, 2024
typescript
(번역) 스트리밍 HTML과 DOM 비교 알고리즘

원글: https://aralroca.com/blog/html-node-streaming 썸네일 최근 몇 년간 브라우저는 HTML과 자바스크립트를 스트리밍을 지원하기 시작했습니다. 이 글에서는 이에 대한 장점과 브라우저가 자동으로 수행하지 않는 다른 작업들을 통해 스트리밍의 이점을 최대한 활용할 수 있는 방법에 대해 이야기할 것입니다. 스트리밍 HTML 초기 로드하는 동안에는 브라우저가 자동으로 처리하기 때문에 크게 신경 쓸 필요가 없습니다. 스트리밍 하는 동안 HTML 청크를 받으면, 브라우저는 그 콘텐츠를 출력합니다. 서버에서 스트리밍을 활성화하려면 헤더를 조정해야 합니다. 아래는 예시입니다. 그리고 응답에서는 ReadableStream을 사용합니다. 이것은 Bun을 사용한 예시입니다. enqueue 안에 있는 각 문자열은 브라우저가 받게 될 청크입니다. 스트리밍 중 HTML 콘텐츠 변경 성능상의 이점이 많기 때문에 많이 사용되는 방법 중 하나는 스트리밍 되는 동안 HTML 콘…

March 21, 2024
translate
react
브라우저 위치 및 크기 관련 API들

프론트 개발을 하다보면 웹 페이지의 다양한 요소들을 직접 제어해야 하는 경우가 있습니다. 이 글에서는 CSSOM의 네 가지 주요 좌표 체계인 Offset, Page, Viewport, Screen에 대해 자세히 알아보고, 위치와 크기 관련 API는 어떤것이 있는지 살펴볼 예정입니다. 이 글을 통해 웹 페이지 내에서 요소의 위치와 크기를 정밀하게 제어하는 데 도움이 되길 바랍니다. 표준 CSSOM 좌표 체계 보통 클라이언트 개발에서 좌표의 원점은 왼쪽 하단이 아닌, 왼쪽 상단 입니다. CSS 객체 모델에서는 Offset, Page, Viewport, Screen 네 가지 표준 좌표 체계가 있습니다. 출처: MDN Coordinate_systems Offset 정의: 오프셋 좌표 체계는 특정 요소의 상대적 위치를 나타내는 데 사용됩니다. 이는 요소의 왼쪽 상단 모서리를 시작점(원점)으로 합니다. 특징: 이 좌표 체계는 요소의 부모 요소를 기준으로 한 상대적 위치를 제공합니다. 마…

March 03, 2024
browser
docker에서 자주쓰는 명령어

Docker 엔진 시작하기/종료하기 도커 엔진: 자동 실행 설정 활성화시, 컴퓨터를 켰을 때 함께 자동으로 실행 Docker Desktop은 도커 엔진 자동으로 실행하도록 설정 되어있음. 리눅스 명령어로 엔진 시작, 종료, 자동 실행 설정 방법 systemctrl: 서비스 시작 및 중지 관련 리눅스 명령어 (system control) 컨테이너: 실행시킬 스크립트가 따로 필요. 컴퓨터를 켰을 때 함께 자동으로 실행 되지 않음. 컨테이너의 기본적인 사용 방법 컨테이너를 다루는 모든 명령어는 docker 명령어로 시작 도커 명령어 구성: docker 커맨드(무엇을 어떻게) 대상 + 옵션, 인자: docker 커맨드(무엇을 어떻게) 옵션 대상 인자 상위 커맨드는 생략 가능하다?! ex) docker run, docker start 커맨드(무엇을 어떻게)는 상위커맨드와 하위커맨드로 나뉘어짐. container 상위 커맨들를 생략하는 경우가 있음. 도커 1.13부터 커맨드가 재편되면서 …

February 18, 2024
infra
(번역) 접근성을 고려하여 CSS 작성하기

원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939 CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다. 이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다. 읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다. 약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리…

February 18, 2024
translate
a11y
(번역) 리액트 서버 컴포넌트를 위한 스토리북

원문: https://storybook.js.org/blog/storybook-react-server-components/ 스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용 리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다. 가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다. 이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 …

February 02, 2024
translate
react
Next.js app router에서 React Query 사용하면서 고민했던 것들

지난 글에서 react-query의 hydrate, dehydrate을 통해 서버에서 prefetching 한 데이터 사용하는 방법에 대해서 살펴보았습니다. 서버에서 prefetching 한 데이터 사용하기 오늘은 조금 실용적으로 Next.js 13, 14 버전의 app router에서 react-query를 어떻게 사용하고 세팅하면 좋을지 고민했던 내용에 대해서 이야기해보도록 하겠습니다. 해당 글은 23년 10월에 메이저 버전 업데이트된, Next.js 14와 React-Query 5 를 기준으로 작성된 글입니다. 예시로 보여주는 코드는 저의 next-14-react-query repo에서 확인 할 수 있습니다. 제가 크게 고민했던 문제는 아래 세 가지입니다. Hydrate vs ReactQueryStreamedHydration 두 가지 API 중에 어떤 것을 채택할까? Hydration API 사용 시에 RSC, RCC 모두에서 깔끔한 코드를 유지하려면 queryOption은 …

January 07, 2024
react
react-query
(번역) 2024년 자바스크립트의 5가지 가장 혁신적인 제안

원문: https://javascript.plainenglish.io/the-5-most-innovative-proposals-for-javascript-planned-for-2024-22139dd2f546 JavaScript Logo 2024 자바스크립트가 빠른 속도로 발전함에 따라 2024년은 이 프로그래밍 언어에 있어 중요한 해가 될 것입니다. 이 글에서는 개발자들이 자바스크립트 코딩에 접근하는 방식을 재정의하기 위한 최신 제안을 자세히 살펴보겠습니다. 이 글이 유용하길 바랍니다! 시작해 봅시다! 목차 데코레이터 Temporal API 파이프라인 연산자 Error cause 레코드와 튜플 1. 데코레이터 자바스크립트에 데코레이터가 도입된 것은 개발자가 클래스, 메서드, 프로퍼티 및 매개변수의 동작을 조작하고 개선할 수 있는 방법이 크게 발전했음을 의미합니다. 파이썬과 타입스크립트와 같은 다른 프로그래밍 언어에서 유래한 데코레이터는 기능을 추가하거나 수정하는 간결하고 선언적인…

December 20, 2023
translate
javascript
서버에서 React Query prefetching 한 데이터 사용하기

Next.js나 Remix 같은 프레임워크 내에서 React-Query를 사용한다면, 서버 렌더링 될 때 요청 후 응답받은 데이터를 SPA 방식으로 전환되고 나서도 유지할 수 있을까요? 어떻게 가능할까요? React Query의 hydrate와 dehydrate는 서버에서 미리 가져온 데이터를 클라이언트 사이드에서 재사용 할 수 있게 해줍니다. 이번 글을 통해 서버 렌더링과 어떻게 이를 가능하게 하는지 hydrate와 dehydrate에 대해서 알아봅시다. Server Rendering 서버 렌더링은 사용자가 페이지를 로드하는 즉시 볼 수 있는 초기 HTML을 서버에서 생성하는 행위입니다. 이는 페이지 요청 시 즉시 발생할 수 있으며(SSR), 이전 요청이 캐시 되었거나 빌드 시간에 미리 생성(SSG) 할 수도 있습니다. 클라이언트 렌더링 애플리케이션에서는 사용자에게 화면에 콘텐츠를 표시하기 전에 최소 3번의 서버 왕복(roundtrips)이 필요합니다. 서버 렌더링은 위의 과정…

December 10, 2023
react
react-query
(번역) 시그널(Signal)에 대한 소개

원문: https://preactjs.com/blog/introducing-signals/ 시그널은 앱이 복잡해져도 빠른 속도를 유지하도록 하는 상태 표현 방식입니다. 시그널은 반응형 원칙에 기반을 두고 있으며, 가상 돔에 최적화된 독특한 구현을 통해 개발자에게 훌륭한 경험을 제공합니다. 본질적으로 시그널은 특정 값을 가지고 있는 .value 속성을 가진 객체입니다. 컴포넌트 내에서 시그널의 value 속성에 접근하면, 그 시그널의 값이 변경될 때 해당 컴포넌트가 자동으로 업데이트됩니다. 이는 간단하고 작성하기 쉬울 뿐만 아니라, 앱이 얼마나 많은 컴포넌트를 가지고 있든 상태 업데이트가 빠르게 유지되도록 보장합니다. 시그널은 기본적으로 빠르며, 백그라운드에서 자동으로 업데이트를 최적화해줍니다. REPL에서 실행 시그널은 훅과 달리 컴포넌트 내부 또는 외부에서 사용할 수 있습니다. 또한 시그널은 훅과 클래스 컴포넌트 모두에서 훌륭하게 작동하므로, 기존 지식을 활용하며 자신의 속도에 …

November 27, 2023
translate
react
(번역) React에서 UI와 로직 분리하기:헤드리스 컴포넌트를 사용한 클린 코드 접근법

원글: https://itnext.io/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components-82e46b5820c 프런트엔드 개발 영역에서는 용어와 패러다임이 때로는 이해하기 어려울 수 있으며 ‘헤드리스 UI’ 또는 ‘헤드리스 컴포넌트’도 이 범주에 속할 수 있습니다. 이러한 용어들이 무엇을 의미하는지 궁금해서 고개를 갸웃거리고, 혼자만 그런 것이 아닙니다. 사실, 혼란스러운 이름에도 불구하고 이러한 개념들은 복잡한 사용자 인터페이스 관리를 상당히 단순화할 수 있는 매력적인 전략입니다. 헤드리스 컴포넌트는 난해해 보일 수 있지만, 그 진정한 힘은 유연성, 재사용 가능성, 그리고 코드베이스의 구성과 깔끔함을 향상시킬 수 있는 능력에 있습니다. 이 글에서는 이 패턴이 정확히 무엇인지, 왜 유용한지, 그리고 인터페이스 디자인에 대한 접근 방식을 어떻게 혁신할 수 있는지에 대해 탐구해 볼 것입니다. …

September 13, 2023
translate
react
(번역) 우리들을 위한 디자인 패턴

원글: https://github.com/kamranahmedse/design-patterns-for-humans/blob/master/readme.md 역자주: 이 글은 Kamran Ahmed의 글이며, PHP-7 기준으로 작성된 예제 코드를 타입스크립트로 변경하였습니다. logo 🎉 디자인 패턴에 대한 초간단 설명입니다! 🎉 누구든지 혼란스럽게 만들 수 있는 주제입니다. 이 글에서는 최대한 간단한 방식으로 설명함으로써 당신의 기억 속 (그리고 아마도 제 기억)에 각인시키기 위해 노력할 것입니다. 저의 다른 프로젝트도 확인하고 트위터에서 “안녕”이라고 인사해 보세요. 생성 패턴 구조 패턴 행동 패턴 심플 팩토리(Simple Factory) 어댑터(Adapter) 책임 연쇄(Chain of Responsibility) 팩토리 메서드(Factory Method) 브릿지(Bridge) 명령(Command) 추상 팩토리(Abstract Factory) 컴포지트(Composite) 반복자…

August 27, 2023
translate
cs
자바스크립트 가비지 컬렉션 알고리즘

힙과 스택 메모리의 차이를 살펴보고, 자바스크립트 대표 엔진인 V8의 Resident set 구조와 Minor GC, Major GC 의 동작 알고리즘에 대해서 자세하게 살펴 보도록 하겠습니다. 이 글이 가비지 컬렉터가 어떻게 동작하는지 이해하는데 도움이 되길 바랍니다. V8 메모리 구조 (feat. Resident set) Resident Set은 실행 중인 프로그램의 메모리 관리를 위해 사용되는 용어로, 현재 V8 프로세스가 사용하는 모든 메모리를 나타냅니다. 이것은 JavaScript 코드, 객체, 함수, 변수 등을 저장하는 데 필요한 모든 메모리를 포함합니다. Resident Set은 다음과 같이 여러 세그먼트로 구성됩니다. 출처: memory management in V8 stack vs heap 메모리 공간은 크게 스택 메모리(Stack memory)와 힙 메모리(Heap memory)로 구분되며, 힙 메모리는 다양한 목적으로 서비스하는 여러 공간으로 더 나누어집니다.…

July 14, 2023
javascript
(번역) React Query를 사용하여 서버 상태를 관리하는 방법

원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …

June 21, 2023
translate
react
react-query
(번역) 캐시 시스템 설계할 때 기억해야 할 6가지 캐싱 전략

원글: https://javascript.plainenglish.io/6-caching-strategies-to-remember-while-designing-cache-system-da058a3757cf 캐시 시스템 관련 용어, 읽기 vs 쓰기 중심의 애플리케이션에서 캐싱 전략, 캐시를 무효화하는 방법 등에 대해서 알려드리겠습니다. 개요 캐싱의 목표는 원본 소스에서 데이터를 가져오는 횟수를 줄여 처리 속도를 높이고 대기 시간을 감소시키는 것입니다. 캐싱은 인메모리 캐싱, 디스크 캐싱, 데이터베이스 캐싱, CDN 캐싱과 같은 다양한 수준의 아키텍처에서 구현될 수 있습니다. 데이터는 각각 고유한 이점이 있는 다양한 기술을 사용하여 캐싱할 수 있습니다. 인메모리 캐싱은 컴퓨터의 주 메모리에 데이터를 저장하여 디스크 저장소 보다 빠른 액세스를 제공합니다. 반면 디스크 캐싱은 하드 디스크에 데이터를 저장하므로 주 메모리보다는 느리지만 원격 소스에서 데이터를 가져오는 것보다 빠릅니다. 데이터…

June 13, 2023
translate
cs
Proxy, Reflect와 메타프로그래밍

ES6에서는 Proxy, Reflect 객체를 통해 메타프로그래밍을 할 수 있습니다. 메타프로그래밍이 무엇인지? 그리고 Proxy와 Reflect에 대해서 알아보도록 하겠습니다. Metaprogramming 메타프로그래밍은 프로그램이 자기 자신을 조작할 수 있는 능력을 말합니다. 이를 통해 프로그램은 동적으로 자기 자신을 변화시키거나, 다른 프로그램을 분석하거나, 새로운 코드를 생성할 수 있습니다. 메타프로그래밍을 통해 코드의 유연성과 재사용성을 높일 수 있으며, 런타임에서 객체의 동작을 수정할 수 있어서 더욱 강력한 코드를 작성할 수 있습니다. 메타프로그래밍에서 가장 중요한 세 가지 개념 Introspection Introspection은 코드가 자기 자신을 검사할 수 있는 능력을 의미합니다. 이를 통해 코드의 내부 구조와 데이터를 검사하거나, 코드의 실행 시점에서 객체의 메소드나 프로퍼티에 접근할 수 있습니다. 자바스크립트에서는 ES6부터 Reflect 객체를 제공하여 Intr…

May 07, 2023
javascript
(번역) StructuredClone API를 사용하여 객체를 깊은 복사하는 법

원문: https://blog.openreplay.com/deep-copying-objects-with-the-structuredclone-api/ Deep Copying Objects with the StructuredClone API 개요: 자바스크립트에서 객체를 복사하는 것은 간단하지 않으며, 이는 잘 알려진 문제입니다. 그러나 이 글에서는 해결책을 제공합니다. StructuredClone API를 사용하면 모든 객체를 간단하고 빠르게 복사할 수 있습니다. 자바스크립트에서 객체가 변수에 저장될 때, 해당 변수는 객체의 참조값을 갖습니다. 이는 변수 자체에 객체를 저장하는 것이 아니라, 객체의 메모리 위치를 나타내는 식별자를 저장한다는 것을 의미합니다. 객체의 복사는 원시 타입과는 다른 방식으로 동작합니다. 얕은 복사(Shallow Copy) vs. 깊은 복사(Deep Copy) 자바스크립트에서 값은 두 가지 방법으로 복사할 수 있습니다. 얕은 복사와 깊은 복사입니다. 얕은 복…

May 07, 2023
translate
javascript
날짜 입력 input 인터렉션 개발기

Date와 인연이 깊은지 날짜 선택 UI를 custom 해서 개발해야 하는 경험을 두 번째 하게 되었다. mui datepicker를 붙이기엔 너무 과해서 react-calendar 라이브러리를 사용하려고 했는데, 문제는 input은 함께 딸려오지 않기 때문에 input 관련 interaction은 직접 구현해야 했다. 인 하우스용 서비스라 여러 라이브러리 참고해서 직접 인터렉션을 정의하고 구현해 보았는데 기록 차 경험을 남겨본다! TL;DR; 전체 구현 결과물은 code sandbox에서 확인할 수 있다. DatePicker 컴포넌트를 개발해 보자! (calendar는 라이브러리를 사용) 딱히 input에 대한 큰 요구사항이 없이 기능 개발을 해야 하는 경우 그냥 react-date-picker를 써라 DatePicker 컴포넌트 개발 dependency react-calendar dayjs props 설계 minDate, maxDate: date range를 지원해야하므로 …

April 23, 2023
feature
Recoil 도입기(feat. 폴더구조)

React 18로 버전업 하면서 프로젝트에 Recoil을 도입했는데, 그 과정에서 고민했던 내용을 기록차 남겨본다. TL;DR; 개념 정리나 장단점 비교에 대해서는 이미 알고 계신분들도 있을 것 같아서 폴더구조나 Redux를 대체하는게 맞는가에 대해서만 먼저 간단하게 요약한 내용을 공유한다. 1. 폴더 구조는 data source 별로 나누자 Suggested folder structure for atoms and selectors recoil 폴더 하위에 data source 별로 나누자 data source 하위에 atoms, selectors, hooks를 두자 2. concurrency mode 사용하려면 Redux는 버리고 Recoil로 넘어가야하나? useSyncExternalStore hook 을 사용해서 Redux에서도 동시성 모드를 사용할 수 있다. 이 부분에 대해서 고민해 본 내용은 아래에.. Recoil 도입 배경 Recoil은 Facebook에서 개발한 상태…

April 08, 2023
react
React Query useMutation에서 variable 옵셔널하게 사용하기

Introduction #1077 이슈를 보면 mutateFn의 variable을 필수값으로 변경한 이력을 볼 수 있다. mutation인데 왜 variable이 없냐고? 나의 경우에는 URL의 있는 queryParams를 읽어와서 데이터를 넘겨주면 되기 때문에 useSomething hook 안에서 처리하고 싶었다. 문제상황 mutate() 와 같이 variable에 아무 값도 넣지 않고 mutation 함수를 호출 할 수 없다. TVariables 타입에 undefined도 줘보고 이래저래 별짓을 다해봐도 필수값을 optional 하게 바꿀수는 없었다. 에러 발생 diff --git a/posts/infra/index.html b/posts/infra/index.html index d592d1d2..cc0452e2 100644 --- a/posts/infra/index.html +++ b/posts/infra/index.html @@ -66,4 +66,4 @@ } } }) - Posts

docker에서 자주쓰는 명령어

Docker 엔진 시작하기/종료하기 도커 엔진: 자동 실행 설정 활성화시, 컴퓨터를 켰을 때 함께 자동으로 실행 Docker Desktop은 도커 엔진 자동으로 실행하도록 설정 되어있음. 리눅스 명령어로 엔진 시작, 종료, 자동 실행 설정 방법 systemctrl: 서비스 시작 및 중지 관련 리눅스 명령어 (system control) 컨테이너: 실행시킬 스크립트가 따로 필요. 컴퓨터를 켰을 때 함께 자동으로 실행 되지 않음. 컨테이너의 기본적인 사용 방법 컨테이너를 다루는 모든 명령어는 docker 명령어로 시작 도커 명령어 구성: docker 커맨드(무엇을 어떻게) 대상 + 옵션, 인자: docker 커맨드(무엇을 어떻게) 옵션 대상 인자 상위 커맨드는 생략 가능하다?! ex) docker run, docker start 커맨드(무엇을 어떻게)는 상위커맨드와 하위커맨드로 나뉘어짐. container 상위 커맨들를 생략하는 경우가 있음. 도커 1.13부터 커맨드가 재편되면서 …

February 18, 2024
infra
Bitbucket과 Jenkins를 연동하여 테스트 자동화 (feat. Generic Webhook Trigger)

목표 Jenkins pipeline을 이용하여, bitbucket에서 PR 생성시 Jenkins job을 실행시키고 그 결과를 slack으로 전송한다. (Jenkins 내의 credential 설정이나 자세한 Jenkins 플러그인 설정 사항은 해당 글에서 포함하지 않음.) Webhook 웹훅이란 뭘까? 위의 목표를 이루려면 우리는 웹훅이 필요하다. 어플리케이션에 한정하여 특정 이벤트가 발생할 때 지정된 URL로 push 알림을 보내는 것이 webhook이다. callback 이랑 비슷한데, 조금 더 작은 범위라고 생각하면 된다. 이 방법은 주기적으로 검사하여 변경 사항이 있는지 확인하는 것보다 훨씬 효율적이다. 구현 방법 우리는 bitbucket에서 PR이 생성되었을때 Jenkins 플러그인인 Generic Webhook Trigger를 호출 하고, Jenkins Job 실행이 끝났을 때 slack Webhook을 호출 할 것이다. 1. Jekins에서 Generic Webho…

September 04, 2022
infra
\ No newline at end of file + Posts
docker에서 자주쓰는 명령어

Docker 엔진 시작하기/종료하기 도커 엔진: 자동 실행 설정 활성화시, 컴퓨터를 켰을 때 함께 자동으로 실행 Docker Desktop은 도커 엔진 자동으로 실행하도록 설정 되어있음. 리눅스 명령어로 엔진 시작, 종료, 자동 실행 설정 방법 systemctrl: 서비스 시작 및 중지 관련 리눅스 명령어 (system control) 컨테이너: 실행시킬 스크립트가 따로 필요. 컴퓨터를 켰을 때 함께 자동으로 실행 되지 않음. 컨테이너의 기본적인 사용 방법 컨테이너를 다루는 모든 명령어는 docker 명령어로 시작 도커 명령어 구성: docker 커맨드(무엇을 어떻게) 대상 + 옵션, 인자: docker 커맨드(무엇을 어떻게) 옵션 대상 인자 상위 커맨드는 생략 가능하다?! ex) docker run, docker start 커맨드(무엇을 어떻게)는 상위커맨드와 하위커맨드로 나뉘어짐. container 상위 커맨들를 생략하는 경우가 있음. 도커 1.13부터 커맨드가 재편되면서 …

February 18, 2024
infra
Bitbucket과 Jenkins를 연동하여 테스트 자동화 (feat. Generic Webhook Trigger)

목표 Jenkins pipeline을 이용하여, bitbucket에서 PR 생성시 Jenkins job을 실행시키고 그 결과를 slack으로 전송한다. (Jenkins 내의 credential 설정이나 자세한 Jenkins 플러그인 설정 사항은 해당 글에서 포함하지 않음.) Webhook 웹훅이란 뭘까? 위의 목표를 이루려면 우리는 웹훅이 필요하다. 어플리케이션에 한정하여 특정 이벤트가 발생할 때 지정된 URL로 push 알림을 보내는 것이 webhook이다. callback 이랑 비슷한데, 조금 더 작은 범위라고 생각하면 된다. 이 방법은 주기적으로 검사하여 변경 사항이 있는지 확인하는 것보다 훨씬 효율적이다. 구현 방법 우리는 bitbucket에서 PR이 생성되었을때 Jenkins 플러그인인 Generic Webhook Trigger를 호출 하고, Jenkins Job 실행이 끝났을 때 slack Webhook을 호출 할 것이다. 1. Jekins에서 Generic Webho…

September 04, 2022
infra
\ No newline at end of file diff --git a/posts/javascript/index.html b/posts/javascript/index.html index 1b9eaa55..a093f0e9 100644 --- a/posts/javascript/index.html +++ b/posts/javascript/index.html @@ -66,5 +66,5 @@ } } }) - Posts
(번역) 2024년 자바스크립트의 5가지 가장 혁신적인 제안

원문: https://javascript.plainenglish.io/the-5-most-innovative-proposals-for-javascript-planned-for-2024-22139dd2f546 JavaScript Logo 2024 자바스크립트가 빠른 속도로 발전함에 따라 2024년은 이 프로그래밍 언어에 있어 중요한 해가 될 것입니다. 이 글에서는 개발자들이 자바스크립트 코딩에 접근하는 방식을 재정의하기 위한 최신 제안을 자세히 살펴보겠습니다. 이 글이 유용하길 바랍니다! 시작해 봅시다! 목차 데코레이터 Temporal API 파이프라인 연산자 Error cause 레코드와 튜플 1. 데코레이터 자바스크립트에 데코레이터가 도입된 것은 개발자가 클래스, 메서드, 프로퍼티 및 매개변수의 동작을 조작하고 개선할 수 있는 방법이 크게 발전했음을 의미합니다. 파이썬과 타입스크립트와 같은 다른 프로그래밍 언어에서 유래한 데코레이터는 기능을 추가하거나 수정하는 간결하고 선언적인…

December 20, 2023
translate
javascript
자바스크립트 가비지 컬렉션 알고리즘

힙과 스택 메모리의 차이를 살펴보고, 자바스크립트 대표 엔진인 V8의 Resident set 구조와 Minor GC, Major GC 의 동작 알고리즘에 대해서 자세하게 살펴 보도록 하겠습니다. 이 글이 가비지 컬렉터가 어떻게 동작하는지 이해하는데 도움이 되길 바랍니다. V8 메모리 구조 (feat. Resident set) Resident Set은 실행 중인 프로그램의 메모리 관리를 위해 사용되는 용어로, 현재 V8 프로세스가 사용하는 모든 메모리를 나타냅니다. 이것은 JavaScript 코드, 객체, 함수, 변수 등을 저장하는 데 필요한 모든 메모리를 포함합니다. Resident Set은 다음과 같이 여러 세그먼트로 구성됩니다. + Posts

(번역) 2024년 자바스크립트의 5가지 가장 혁신적인 제안

원문: https://javascript.plainenglish.io/the-5-most-innovative-proposals-for-javascript-planned-for-2024-22139dd2f546 JavaScript Logo 2024 자바스크립트가 빠른 속도로 발전함에 따라 2024년은 이 프로그래밍 언어에 있어 중요한 해가 될 것입니다. 이 글에서는 개발자들이 자바스크립트 코딩에 접근하는 방식을 재정의하기 위한 최신 제안을 자세히 살펴보겠습니다. 이 글이 유용하길 바랍니다! 시작해 봅시다! 목차 데코레이터 Temporal API 파이프라인 연산자 Error cause 레코드와 튜플 1. 데코레이터 자바스크립트에 데코레이터가 도입된 것은 개발자가 클래스, 메서드, 프로퍼티 및 매개변수의 동작을 조작하고 개선할 수 있는 방법이 크게 발전했음을 의미합니다. 파이썬과 타입스크립트와 같은 다른 프로그래밍 언어에서 유래한 데코레이터는 기능을 추가하거나 수정하는 간결하고 선언적인…

December 20, 2023
translate
javascript
자바스크립트 가비지 컬렉션 알고리즘

힙과 스택 메모리의 차이를 살펴보고, 자바스크립트 대표 엔진인 V8의 Resident set 구조와 Minor GC, Major GC 의 동작 알고리즘에 대해서 자세하게 살펴 보도록 하겠습니다. 이 글이 가비지 컬렉터가 어떻게 동작하는지 이해하는데 도움이 되길 바랍니다. V8 메모리 구조 (feat. Resident set) Resident Set은 실행 중인 프로그램의 메모리 관리를 위해 사용되는 용어로, 현재 V8 프로세스가 사용하는 모든 메모리를 나타냅니다. 이것은 JavaScript 코드, 객체, 함수, 변수 등을 저장하는 데 필요한 모든 메모리를 포함합니다. Resident Set은 다음과 같이 여러 세그먼트로 구성됩니다. 출처: memory management in V8 stack vs heap 메모리 공간은 크게 스택 메모리(Stack memory)와 힙 메모리(Heap memory)로 구분되며, 힙 메모리는 다양한 목적으로 서비스하는 여러 공간으로 더 나누어집니다.…

July 14, 2023
javascript
Proxy, Reflect와 메타프로그래밍

ES6에서는 Proxy, Reflect 객체를 통해 메타프로그래밍을 할 수 있습니다. 메타프로그래밍이 무엇인지? 그리고 Proxy와 Reflect에 대해서 알아보도록 하겠습니다. Metaprogramming 메타프로그래밍은 프로그램이 자기 자신을 조작할 수 있는 능력을 말합니다. 이를 통해 프로그램은 동적으로 자기 자신을 변화시키거나, 다른 프로그램을 분석하거나, 새로운 코드를 생성할 수 있습니다. 메타프로그래밍을 통해 코드의 유연성과 재사용성을 높일 수 있으며, 런타임에서 객체의 동작을 수정할 수 있어서 더욱 강력한 코드를 작성할 수 있습니다. 메타프로그래밍에서 가장 중요한 세 가지 개념 Introspection Introspection은 코드가 자기 자신을 검사할 수 있는 능력을 의미합니다. 이를 통해 코드의 내부 구조와 데이터를 검사하거나, 코드의 실행 시점에서 객체의 메소드나 프로퍼티에 접근할 수 있습니다. 자바스크립트에서는 ES6부터 Reflect 객체를 제공하여 Intr…

May 07, 2023
javascript
(번역) StructuredClone API를 사용하여 객체를 깊은 복사하는 법

원문: https://blog.openreplay.com/deep-copying-objects-with-the-structuredclone-api/ Deep Copying Objects with the StructuredClone API 개요: 자바스크립트에서 객체를 복사하는 것은 간단하지 않으며, 이는 잘 알려진 문제입니다. 그러나 이 글에서는 해결책을 제공합니다. StructuredClone API를 사용하면 모든 객체를 간단하고 빠르게 복사할 수 있습니다. 자바스크립트에서 객체가 변수에 저장될 때, 해당 변수는 객체의 참조값을 갖습니다. 이는 변수 자체에 객체를 저장하는 것이 아니라, 객체의 메모리 위치를 나타내는 식별자를 저장한다는 것을 의미합니다. 객체의 복사는 원시 타입과는 다른 방식으로 동작합니다. 얕은 복사(Shallow Copy) vs. 깊은 복사(Deep Copy) 자바스크립트에서 값은 두 가지 방법으로 복사할 수 있습니다. 얕은 복사와 깊은 복사입니다. 얕은 복…

May 07, 2023
translate
javascript
\ No newline at end of file diff --git a/posts/next/index.html b/posts/next/index.html index 179db2af..4b5b178f 100644 --- a/posts/next/index.html +++ b/posts/next/index.html @@ -66,4 +66,4 @@ } } }) - Posts \ No newline at end of file + Posts \ No newline at end of file diff --git a/posts/react-query/index.html b/posts/react-query/index.html index a5cc731b..612b79dc 100644 --- a/posts/react-query/index.html +++ b/posts/react-query/index.html @@ -66,6 +66,6 @@ } } }) - Posts
Next.js app router에서 React Query 사용하면서 고민했던 것들

지난 글에서 react-query의 hydrate, dehydrate을 통해 서버에서 prefetching 한 데이터 사용하는 방법에 대해서 살펴보았습니다. 서버에서 prefetching 한 데이터 사용하기 오늘은 조금 실용적으로 Next.js 13, 14 버전의 app router에서 react-query를 어떻게 사용하고 세팅하면 좋을지 고민했던 내용에 대해서 이야기해보도록 하겠습니다. 해당 글은 23년 10월에 메이저 버전 업데이트된, Next.js 14와 React-Query 5 를 기준으로 작성된 글입니다. 예시로 보여주는 코드는 저의 next-14-react-query repo에서 확인 할 수 있습니다. 제가 크게 고민했던 문제는 아래 세 가지입니다. Hydrate vs ReactQueryStreamedHydration 두 가지 API 중에 어떤 것을 채택할까? Hydration API 사용 시에 RSC, RCC 모두에서 깔끔한 코드를 유지하려면 queryOption은 …

January 07, 2024
react
react-query
서버에서 React Query prefetching 한 데이터 사용하기

Next.js나 Remix 같은 프레임워크 내에서 React-Query를 사용한다면, 서버 렌더링 될 때 요청 후 응답받은 데이터를 SPA 방식으로 전환되고 나서도 유지할 수 있을까요? 어떻게 가능할까요? React Query의 hydrate와 dehydrate는 서버에서 미리 가져온 데이터를 클라이언트 사이드에서 재사용 할 수 있게 해줍니다. 이번 글을 통해 서버 렌더링과 어떻게 이를 가능하게 하는지 hydrate와 dehydrate에 대해서 알아봅시다. Server Rendering 서버 렌더링은 사용자가 페이지를 로드하는 즉시 볼 수 있는 초기 HTML을 서버에서 생성하는 행위입니다. 이는 페이지 요청 시 즉시 발생할 수 있으며(SSR), 이전 요청이 캐시 되었거나 빌드 시간에 미리 생성(SSG) 할 수도 있습니다. 클라이언트 렌더링 애플리케이션에서는 사용자에게 화면에 콘텐츠를 표시하기 전에 최소 3번의 서버 왕복(roundtrips)이 필요합니다. 서버 렌더링은 위의 과정…

December 10, 2023
react
react-query
(번역) React Query를 사용하여 서버 상태를 관리하는 방법

원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …

June 21, 2023
translate
react
react-query
React Query useMutation에서 variable 옵셔널하게 사용하기

Introduction #1077 이슈를 보면 mutateFn의 variable을 필수값으로 변경한 이력을 볼 수 있다. mutation인데 왜 variable이 없냐고? 나의 경우에는 URL의 있는 queryParams를 읽어와서 데이터를 넘겨주면 되기 때문에 useSomething hook 안에서 처리하고 싶었다. 문제상황 mutate() 와 같이 variable에 아무 값도 넣지 않고 mutation 함수를 호출 할 수 없다. + Posts

Next.js app router에서 React Query 사용하면서 고민했던 것들

지난 글에서 react-query의 hydrate, dehydrate을 통해 서버에서 prefetching 한 데이터 사용하는 방법에 대해서 살펴보았습니다. 서버에서 prefetching 한 데이터 사용하기 오늘은 조금 실용적으로 Next.js 13, 14 버전의 app router에서 react-query를 어떻게 사용하고 세팅하면 좋을지 고민했던 내용에 대해서 이야기해보도록 하겠습니다. 해당 글은 23년 10월에 메이저 버전 업데이트된, Next.js 14와 React-Query 5 를 기준으로 작성된 글입니다. 예시로 보여주는 코드는 저의 next-14-react-query repo에서 확인 할 수 있습니다. 제가 크게 고민했던 문제는 아래 세 가지입니다. Hydrate vs ReactQueryStreamedHydration 두 가지 API 중에 어떤 것을 채택할까? Hydration API 사용 시에 RSC, RCC 모두에서 깔끔한 코드를 유지하려면 queryOption은 …

January 07, 2024
react
react-query
서버에서 React Query prefetching 한 데이터 사용하기

Next.js나 Remix 같은 프레임워크 내에서 React-Query를 사용한다면, 서버 렌더링 될 때 요청 후 응답받은 데이터를 SPA 방식으로 전환되고 나서도 유지할 수 있을까요? 어떻게 가능할까요? React Query의 hydrate와 dehydrate는 서버에서 미리 가져온 데이터를 클라이언트 사이드에서 재사용 할 수 있게 해줍니다. 이번 글을 통해 서버 렌더링과 어떻게 이를 가능하게 하는지 hydrate와 dehydrate에 대해서 알아봅시다. Server Rendering 서버 렌더링은 사용자가 페이지를 로드하는 즉시 볼 수 있는 초기 HTML을 서버에서 생성하는 행위입니다. 이는 페이지 요청 시 즉시 발생할 수 있으며(SSR), 이전 요청이 캐시 되었거나 빌드 시간에 미리 생성(SSG) 할 수도 있습니다. 클라이언트 렌더링 애플리케이션에서는 사용자에게 화면에 콘텐츠를 표시하기 전에 최소 3번의 서버 왕복(roundtrips)이 필요합니다. 서버 렌더링은 위의 과정…

December 10, 2023
react
react-query
(번역) React Query를 사용하여 서버 상태를 관리하는 방법

원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …

June 21, 2023
translate
react
react-query
React Query useMutation에서 variable 옵셔널하게 사용하기

Introduction #1077 이슈를 보면 mutateFn의 variable을 필수값으로 변경한 이력을 볼 수 있다. mutation인데 왜 variable이 없냐고? 나의 경우에는 URL의 있는 queryParams를 읽어와서 데이터를 넘겨주면 되기 때문에 useSomething hook 안에서 처리하고 싶었다. 문제상황 mutate() 와 같이 variable에 아무 값도 넣지 않고 mutation 함수를 호출 할 수 없다. TVariables 타입에 undefined도 줘보고 이래저래 별짓을 다해봐도 필수값을 optional 하게 바꿀수는 없었다. 에러 발생 해결 실질적으로는 mute 함수에서 variable을 정의해서 넣어주는 것이기 때문에 그렇게 꼼수해결법도 아니다.

February 15, 2023
react
react-query
troubleshooting
React Query 시작하기 (feat. Tanstack)

Introduction 이 글을 읽기전 React의 상태관리 종류 4가지를 먼저 읽으면 이해가 쉬울 수 있다. 얼마전 회사 프로젝트에 react-query를 도입하여 사용중인데 정말 너무너무 좋다. react-query를 이용해서 server state를 분리하다보니 loading 처리나, refetch 등 불필요한 코드가 많이 줄었다. 뭐든지 처음이 어렵지 큰 그림을 알고있으면 도입하여 적용하는데에는 큰 문제가 없고, 사용하면서 상황에 맞게 옵션과 아키텍쳐를 변경하면 된다고 생각하기 때문에, 이번 포스팅에서는 react-query를 적용하기 위해 필요한 전반적인 개념을 정리해보고자 한다. 용도 react-query는 서버 데이터 관리를 위한 라이브러리다. 이 라이브러리는 API 호출, 캐시 및 상태 관리, 오류 처리 등의 작업을 간단하게 처리할 수 있도록 도와준다. 서버 데이터 관리는 중요한 부분 중 하나다. 클라이언트에서는 서버로부터 데이터를 가져와(Read) 화면에 그려주고…

February 05, 2023
react
react-query
\ No newline at end of file diff --git a/posts/react/index.html b/posts/react/index.html index 37e9faa1..3265515d 100644 --- a/posts/react/index.html +++ b/posts/react/index.html @@ -66,7 +66,7 @@ } } }) - Posts
환경 변수(process.env)를 구조 분해 할당하면 안되는 이유

process.env 값을 구조 분해 할당 하셨나요? 우리가 환경변수를 접근할 때 객체의 속성값에 접근할 때 사용하는 점 표기법을 사용하기 때문에 구조 분해 할당을 자연스럽게 사용해도 될 것 같은데요. 이런 경우, 왜 에러가 발생하는지 이번 글을 통해 알아보겠습니다. 문제 상황 다음과 같은 코드가 있다고 가정해 봅시다. 위 코드는 process.env 객체에서 API_KEY와 NEXT_PUBLIC_ANALYTICS_ID 변수를 구조 분해 할당하여 사용하려고 합니다. 그러나 Next.js에서는 이 코드가 제대로 작동하지 않을 수 있고, 오류가 발생할 수 있습니다. 왜 이런 문제가 발생할까요? process.env는 일반적인 자바스크립트 객체와는 다르기 때문입니다. 이는 Node.js 환경에서 환경 변수를 담고 있는 특수한 객체로, 모든 속성이 문자열 형태로 저장됩니다. Next.js는 빌드 타임과 런타임에서 환경 변수에 접근하는 방식이 다르므로, 이로 인해 구조 분해 할당을 사용할 …

June 26, 2024
react
next
(번역) 스트리밍 HTML과 DOM 비교 알고리즘

원글: https://aralroca.com/blog/html-node-streaming 썸네일 최근 몇 년간 브라우저는 HTML과 자바스크립트를 스트리밍을 지원하기 시작했습니다. 이 글에서는 이에 대한 장점과 브라우저가 자동으로 수행하지 않는 다른 작업들을 통해 스트리밍의 이점을 최대한 활용할 수 있는 방법에 대해 이야기할 것입니다. 스트리밍 HTML 초기 로드하는 동안에는 브라우저가 자동으로 처리하기 때문에 크게 신경 쓸 필요가 없습니다. 스트리밍 하는 동안 HTML 청크를 받으면, 브라우저는 그 콘텐츠를 출력합니다. 서버에서 스트리밍을 활성화하려면 헤더를 조정해야 합니다. 아래는 예시입니다. 그리고 응답에서는 ReadableStream을 사용합니다. 이것은 Bun을 사용한 예시입니다. enqueue 안에 있는 각 문자열은 브라우저가 받게 될 청크입니다. 스트리밍 중 HTML 콘텐츠 변경 성능상의 이점이 많기 때문에 많이 사용되는 방법 중 하나는 스트리밍 되는 동안 HTML 콘…

March 21, 2024
translate
react
(번역) 리액트 서버 컴포넌트를 위한 스토리북

원문: https://storybook.js.org/blog/storybook-react-server-components/ 스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용 리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다. 가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다. 이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 …

February 02, 2024
translate
react
Next.js app router에서 React Query 사용하면서 고민했던 것들

지난 글에서 react-query의 hydrate, dehydrate을 통해 서버에서 prefetching 한 데이터 사용하는 방법에 대해서 살펴보았습니다. 서버에서 prefetching 한 데이터 사용하기 오늘은 조금 실용적으로 Next.js 13, 14 버전의 app router에서 react-query를 어떻게 사용하고 세팅하면 좋을지 고민했던 내용에 대해서 이야기해보도록 하겠습니다. 해당 글은 23년 10월에 메이저 버전 업데이트된, Next.js 14와 React-Query 5 를 기준으로 작성된 글입니다. 예시로 보여주는 코드는 저의 next-14-react-query repo에서 확인 할 수 있습니다. 제가 크게 고민했던 문제는 아래 세 가지입니다. Hydrate vs ReactQueryStreamedHydration 두 가지 API 중에 어떤 것을 채택할까? Hydration API 사용 시에 RSC, RCC 모두에서 깔끔한 코드를 유지하려면 queryOption은 …

January 07, 2024
react
react-query
서버에서 React Query prefetching 한 데이터 사용하기

Next.js나 Remix 같은 프레임워크 내에서 React-Query를 사용한다면, 서버 렌더링 될 때 요청 후 응답받은 데이터를 SPA 방식으로 전환되고 나서도 유지할 수 있을까요? 어떻게 가능할까요? React Query의 hydrate와 dehydrate는 서버에서 미리 가져온 데이터를 클라이언트 사이드에서 재사용 할 수 있게 해줍니다. 이번 글을 통해 서버 렌더링과 어떻게 이를 가능하게 하는지 hydrate와 dehydrate에 대해서 알아봅시다. Server Rendering 서버 렌더링은 사용자가 페이지를 로드하는 즉시 볼 수 있는 초기 HTML을 서버에서 생성하는 행위입니다. 이는 페이지 요청 시 즉시 발생할 수 있으며(SSR), 이전 요청이 캐시 되었거나 빌드 시간에 미리 생성(SSG) 할 수도 있습니다. 클라이언트 렌더링 애플리케이션에서는 사용자에게 화면에 콘텐츠를 표시하기 전에 최소 3번의 서버 왕복(roundtrips)이 필요합니다. 서버 렌더링은 위의 과정…

December 10, 2023
react
react-query
(번역) 시그널(Signal)에 대한 소개

원문: https://preactjs.com/blog/introducing-signals/ 시그널은 앱이 복잡해져도 빠른 속도를 유지하도록 하는 상태 표현 방식입니다. 시그널은 반응형 원칙에 기반을 두고 있으며, 가상 돔에 최적화된 독특한 구현을 통해 개발자에게 훌륭한 경험을 제공합니다. 본질적으로 시그널은 특정 값을 가지고 있는 .value 속성을 가진 객체입니다. 컴포넌트 내에서 시그널의 value 속성에 접근하면, 그 시그널의 값이 변경될 때 해당 컴포넌트가 자동으로 업데이트됩니다. 이는 간단하고 작성하기 쉬울 뿐만 아니라, 앱이 얼마나 많은 컴포넌트를 가지고 있든 상태 업데이트가 빠르게 유지되도록 보장합니다. 시그널은 기본적으로 빠르며, 백그라운드에서 자동으로 업데이트를 최적화해줍니다. REPL에서 실행 시그널은 훅과 달리 컴포넌트 내부 또는 외부에서 사용할 수 있습니다. 또한 시그널은 훅과 클래스 컴포넌트 모두에서 훌륭하게 작동하므로, 기존 지식을 활용하며 자신의 속도에 …

November 27, 2023
translate
react
(번역) React에서 UI와 로직 분리하기:헤드리스 컴포넌트를 사용한 클린 코드 접근법

원글: https://itnext.io/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components-82e46b5820c 프런트엔드 개발 영역에서는 용어와 패러다임이 때로는 이해하기 어려울 수 있으며 ‘헤드리스 UI’ 또는 ‘헤드리스 컴포넌트’도 이 범주에 속할 수 있습니다. 이러한 용어들이 무엇을 의미하는지 궁금해서 고개를 갸웃거리고, 혼자만 그런 것이 아닙니다. 사실, 혼란스러운 이름에도 불구하고 이러한 개념들은 복잡한 사용자 인터페이스 관리를 상당히 단순화할 수 있는 매력적인 전략입니다. 헤드리스 컴포넌트는 난해해 보일 수 있지만, 그 진정한 힘은 유연성, 재사용 가능성, 그리고 코드베이스의 구성과 깔끔함을 향상시킬 수 있는 능력에 있습니다. 이 글에서는 이 패턴이 정확히 무엇인지, 왜 유용한지, 그리고 인터페이스 디자인에 대한 접근 방식을 어떻게 혁신할 수 있는지에 대해 탐구해 볼 것입니다. …

September 13, 2023
translate
react
(번역) React Query를 사용하여 서버 상태를 관리하는 방법

원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …

June 21, 2023
translate
react
react-query
Recoil 도입기(feat. 폴더구조)

React 18로 버전업 하면서 프로젝트에 Recoil을 도입했는데, 그 과정에서 고민했던 내용을 기록차 남겨본다. + Posts

환경 변수(process.env)를 구조 분해 할당하면 안되는 이유

process.env 값을 구조 분해 할당 하셨나요? 우리가 환경변수를 접근할 때 객체의 속성값에 접근할 때 사용하는 점 표기법을 사용하기 때문에 구조 분해 할당을 자연스럽게 사용해도 될 것 같은데요. 이런 경우, 왜 에러가 발생하는지 이번 글을 통해 알아보겠습니다. 문제 상황 다음과 같은 코드가 있다고 가정해 봅시다. 위 코드는 process.env 객체에서 API_KEY와 NEXT_PUBLIC_ANALYTICS_ID 변수를 구조 분해 할당하여 사용하려고 합니다. 그러나 Next.js에서는 이 코드가 제대로 작동하지 않을 수 있고, 오류가 발생할 수 있습니다. 왜 이런 문제가 발생할까요? process.env는 일반적인 자바스크립트 객체와는 다르기 때문입니다. 이는 Node.js 환경에서 환경 변수를 담고 있는 특수한 객체로, 모든 속성이 문자열 형태로 저장됩니다. Next.js는 빌드 타임과 런타임에서 환경 변수에 접근하는 방식이 다르므로, 이로 인해 구조 분해 할당을 사용할 …

June 26, 2024
react
next
(번역) 스트리밍 HTML과 DOM 비교 알고리즘

원글: https://aralroca.com/blog/html-node-streaming 썸네일 최근 몇 년간 브라우저는 HTML과 자바스크립트를 스트리밍을 지원하기 시작했습니다. 이 글에서는 이에 대한 장점과 브라우저가 자동으로 수행하지 않는 다른 작업들을 통해 스트리밍의 이점을 최대한 활용할 수 있는 방법에 대해 이야기할 것입니다. 스트리밍 HTML 초기 로드하는 동안에는 브라우저가 자동으로 처리하기 때문에 크게 신경 쓸 필요가 없습니다. 스트리밍 하는 동안 HTML 청크를 받으면, 브라우저는 그 콘텐츠를 출력합니다. 서버에서 스트리밍을 활성화하려면 헤더를 조정해야 합니다. 아래는 예시입니다. 그리고 응답에서는 ReadableStream을 사용합니다. 이것은 Bun을 사용한 예시입니다. enqueue 안에 있는 각 문자열은 브라우저가 받게 될 청크입니다. 스트리밍 중 HTML 콘텐츠 변경 성능상의 이점이 많기 때문에 많이 사용되는 방법 중 하나는 스트리밍 되는 동안 HTML 콘…

March 21, 2024
translate
react
(번역) 리액트 서버 컴포넌트를 위한 스토리북

원문: https://storybook.js.org/blog/storybook-react-server-components/ 스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용 리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다. 가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다. 이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 …

February 02, 2024
translate
react
Next.js app router에서 React Query 사용하면서 고민했던 것들

지난 글에서 react-query의 hydrate, dehydrate을 통해 서버에서 prefetching 한 데이터 사용하는 방법에 대해서 살펴보았습니다. 서버에서 prefetching 한 데이터 사용하기 오늘은 조금 실용적으로 Next.js 13, 14 버전의 app router에서 react-query를 어떻게 사용하고 세팅하면 좋을지 고민했던 내용에 대해서 이야기해보도록 하겠습니다. 해당 글은 23년 10월에 메이저 버전 업데이트된, Next.js 14와 React-Query 5 를 기준으로 작성된 글입니다. 예시로 보여주는 코드는 저의 next-14-react-query repo에서 확인 할 수 있습니다. 제가 크게 고민했던 문제는 아래 세 가지입니다. Hydrate vs ReactQueryStreamedHydration 두 가지 API 중에 어떤 것을 채택할까? Hydration API 사용 시에 RSC, RCC 모두에서 깔끔한 코드를 유지하려면 queryOption은 …

January 07, 2024
react
react-query
서버에서 React Query prefetching 한 데이터 사용하기

Next.js나 Remix 같은 프레임워크 내에서 React-Query를 사용한다면, 서버 렌더링 될 때 요청 후 응답받은 데이터를 SPA 방식으로 전환되고 나서도 유지할 수 있을까요? 어떻게 가능할까요? React Query의 hydrate와 dehydrate는 서버에서 미리 가져온 데이터를 클라이언트 사이드에서 재사용 할 수 있게 해줍니다. 이번 글을 통해 서버 렌더링과 어떻게 이를 가능하게 하는지 hydrate와 dehydrate에 대해서 알아봅시다. Server Rendering 서버 렌더링은 사용자가 페이지를 로드하는 즉시 볼 수 있는 초기 HTML을 서버에서 생성하는 행위입니다. 이는 페이지 요청 시 즉시 발생할 수 있으며(SSR), 이전 요청이 캐시 되었거나 빌드 시간에 미리 생성(SSG) 할 수도 있습니다. 클라이언트 렌더링 애플리케이션에서는 사용자에게 화면에 콘텐츠를 표시하기 전에 최소 3번의 서버 왕복(roundtrips)이 필요합니다. 서버 렌더링은 위의 과정…

December 10, 2023
react
react-query
(번역) 시그널(Signal)에 대한 소개

원문: https://preactjs.com/blog/introducing-signals/ 시그널은 앱이 복잡해져도 빠른 속도를 유지하도록 하는 상태 표현 방식입니다. 시그널은 반응형 원칙에 기반을 두고 있으며, 가상 돔에 최적화된 독특한 구현을 통해 개발자에게 훌륭한 경험을 제공합니다. 본질적으로 시그널은 특정 값을 가지고 있는 .value 속성을 가진 객체입니다. 컴포넌트 내에서 시그널의 value 속성에 접근하면, 그 시그널의 값이 변경될 때 해당 컴포넌트가 자동으로 업데이트됩니다. 이는 간단하고 작성하기 쉬울 뿐만 아니라, 앱이 얼마나 많은 컴포넌트를 가지고 있든 상태 업데이트가 빠르게 유지되도록 보장합니다. 시그널은 기본적으로 빠르며, 백그라운드에서 자동으로 업데이트를 최적화해줍니다. REPL에서 실행 시그널은 훅과 달리 컴포넌트 내부 또는 외부에서 사용할 수 있습니다. 또한 시그널은 훅과 클래스 컴포넌트 모두에서 훌륭하게 작동하므로, 기존 지식을 활용하며 자신의 속도에 …

November 27, 2023
translate
react
(번역) React에서 UI와 로직 분리하기:헤드리스 컴포넌트를 사용한 클린 코드 접근법

원글: https://itnext.io/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components-82e46b5820c 프런트엔드 개발 영역에서는 용어와 패러다임이 때로는 이해하기 어려울 수 있으며 ‘헤드리스 UI’ 또는 ‘헤드리스 컴포넌트’도 이 범주에 속할 수 있습니다. 이러한 용어들이 무엇을 의미하는지 궁금해서 고개를 갸웃거리고, 혼자만 그런 것이 아닙니다. 사실, 혼란스러운 이름에도 불구하고 이러한 개념들은 복잡한 사용자 인터페이스 관리를 상당히 단순화할 수 있는 매력적인 전략입니다. 헤드리스 컴포넌트는 난해해 보일 수 있지만, 그 진정한 힘은 유연성, 재사용 가능성, 그리고 코드베이스의 구성과 깔끔함을 향상시킬 수 있는 능력에 있습니다. 이 글에서는 이 패턴이 정확히 무엇인지, 왜 유용한지, 그리고 인터페이스 디자인에 대한 접근 방식을 어떻게 혁신할 수 있는지에 대해 탐구해 볼 것입니다. …

September 13, 2023
translate
react
(번역) React Query를 사용하여 서버 상태를 관리하는 방법

원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …

June 21, 2023
translate
react
react-query
Recoil 도입기(feat. 폴더구조)

React 18로 버전업 하면서 프로젝트에 Recoil을 도입했는데, 그 과정에서 고민했던 내용을 기록차 남겨본다. TL;DR; 개념 정리나 장단점 비교에 대해서는 이미 알고 계신분들도 있을 것 같아서 폴더구조나 Redux를 대체하는게 맞는가에 대해서만 먼저 간단하게 요약한 내용을 공유한다. 1. 폴더 구조는 data source 별로 나누자 Suggested folder structure for atoms and selectors recoil 폴더 하위에 data source 별로 나누자 data source 하위에 atoms, selectors, hooks를 두자 2. concurrency mode 사용하려면 Redux는 버리고 Recoil로 넘어가야하나? useSyncExternalStore hook 을 사용해서 Redux에서도 동시성 모드를 사용할 수 있다. 이 부분에 대해서 고민해 본 내용은 아래에.. Recoil 도입 배경 Recoil은 Facebook에서 개발한 상태…

April 08, 2023
react
React Query useMutation에서 variable 옵셔널하게 사용하기

Introduction #1077 이슈를 보면 mutateFn의 variable을 필수값으로 변경한 이력을 볼 수 있다. mutation인데 왜 variable이 없냐고? 나의 경우에는 URL의 있는 queryParams를 읽어와서 데이터를 넘겨주면 되기 때문에 useSomething hook 안에서 처리하고 싶었다. 문제상황 mutate() 와 같이 variable에 아무 값도 넣지 않고 mutation 함수를 호출 할 수 없다. TVariables 타입에 undefined도 줘보고 이래저래 별짓을 다해봐도 필수값을 optional 하게 바꿀수는 없었다. 에러 발생 해결 실질적으로는 mute 함수에서 variable을 정의해서 넣어주는 것이기 때문에 그렇게 꼼수해결법도 아니다.

February 15, 2023
react
react-query
troubleshooting
React Query 시작하기 (feat. Tanstack)

Introduction 이 글을 읽기전 React의 상태관리 종류 4가지를 먼저 읽으면 이해가 쉬울 수 있다. 얼마전 회사 프로젝트에 react-query를 도입하여 사용중인데 정말 너무너무 좋다. react-query를 이용해서 server state를 분리하다보니 loading 처리나, refetch 등 불필요한 코드가 많이 줄었다. 뭐든지 처음이 어렵지 큰 그림을 알고있으면 도입하여 적용하는데에는 큰 문제가 없고, 사용하면서 상황에 맞게 옵션과 아키텍쳐를 변경하면 된다고 생각하기 때문에, 이번 포스팅에서는 react-query를 적용하기 위해 필요한 전반적인 개념을 정리해보고자 한다. 용도 react-query는 서버 데이터 관리를 위한 라이브러리다. 이 라이브러리는 API 호출, 캐시 및 상태 관리, 오류 처리 등의 작업을 간단하게 처리할 수 있도록 도와준다. 서버 데이터 관리는 중요한 부분 중 하나다. 클라이언트에서는 서버로부터 데이터를 가져와(Read) 화면에 그려주고…

February 05, 2023
react
react-query
React의 상태관리 종류 4가지

Introduction react의 상태관리 종류 4가지를 알아보고, 각각의 종류별로 왜 다르게 관리해야하는지와 React에서 제공하는 API 혹은 어떤 라이브러리를 사용해서 관리하면 좋을지 알아보도록 하자. 상태관리 종류 Local state Global state App 어느곳(여러 컴포넌트)에서 state를 확인하거나 업데이트하기 위해서 필요함. 예: authenticated user state - logged in/out Server state 서버로 받은 데이터가 UI 상태와 통합되어야 하는 경우 필요함. 예: loading, error… 대표 라이브러리: SWR, react-query URL state URL에 존재하는 데이터(pathname, query params..) 1) Local state useState useReducer useReducer vs useState 차이 useState는 하나의 state operation만 가능했다면, useReducer는 여…

February 03, 2023
react
에러 핸들링에 대한 고민 (feat. React)

고민 혹시 제가 내린 결론에 다른 의견을 주신다면 매우 감사합니다. 🙇🏻‍♀️ 에러를 발생 vs 사전 확인을 통해 예외 처리 어느 부분까지 사전 확인이 필요한 걸까? 고민하게 된 배경 함수의 매개변수가 의도한 것과 다른 값이 들어왔을 때 에러가 발생할 수 있다. 그런데 문득 에러를 발생하게 하는 게 올바른 것인가? 아니면 애초에 에러가 발생할 상황을 하나도 빠짐없이 막는 것이 올바른 것인가? 고민이 들었다. 위의 코드를 보면 event 객체가 무조건 존재할 상황을 가졍하고 있으므로 만약 비정상적인 event 객체가 첫 번째 인자로 전달된다면 에러가 발생할 것이다. 그렇다면 이렇게 코드를 짜는 것이 좋을까? 애초에 event 객체가 존재하지 않을 수 있는 케이스를 고려해서 절대로 에러를 발생하지 않게 하는 것이 좋을까? 결론 App을 Crashed 시키지 않는 한해서 에러가 발생해야 하는 경우는 발생하게 내버려 두고, 예상할 수 있는 예외는 try…catch를 이용하여 에러가 전파되지 않도…

October 16, 2022
react
\ No newline at end of file diff --git a/posts/test/index.html b/posts/test/index.html index 5864cb7f..98bc59c9 100644 --- a/posts/test/index.html +++ b/posts/test/index.html @@ -66,4 +66,4 @@ } } }) - Posts \ No newline at end of file + Posts \ No newline at end of file diff --git a/posts/translate/index.html b/posts/translate/index.html index 23264a99..1b59a083 100644 --- a/posts/translate/index.html +++ b/posts/translate/index.html @@ -66,4 +66,4 @@ } } }) - Posts
(번역) 타입스크립트에서 'As Const' 이해하기

원글: https://www.omarileon.me/blog/typescript-as-const 새로운 구문인 “상수 어설션(const assertions)“은 TypeScript 3.4에서 도입되었습니다. 이것은 변수가 변경되지 않을 것(immutable)이고 가능한 엄격한 타입을 제공해야 한다는 것을 타입스크립트에 알려줍니다. 이는 타입의 종류에 따라 다르게 영향을 미치므로, 이 글에서는 상수 어설션을 어떻게 사용하고 왜 유용한지에 대해 설명할 것입니다. 문자열/숫자 문자열이나 숫자에 “as const”를 추가하면 특정 값으로 타입을 좁힐 수 있습니다. 숫자의 경우 문자열 또는 숫자의 경우 일반적으로 “const”를 사용하여 변수를 정의하면 동일한 효과를 얻을 수 있기 때문에 덜 유용합니다. 런타임 안전성이라는 추가 이점도 있습니다. 때로는 변수로 값을 정의하지 않고, 단순히 문자열 리터럴을 사용하고 싶을 수 있습니다. 예를 들어 값을 반환할 때처럼요. 그때 “as const”…

April 14, 2024
translate
typescript
(번역) 스트리밍 HTML과 DOM 비교 알고리즘

원글: https://aralroca.com/blog/html-node-streaming 썸네일 최근 몇 년간 브라우저는 HTML과 자바스크립트를 스트리밍을 지원하기 시작했습니다. 이 글에서는 이에 대한 장점과 브라우저가 자동으로 수행하지 않는 다른 작업들을 통해 스트리밍의 이점을 최대한 활용할 수 있는 방법에 대해 이야기할 것입니다. 스트리밍 HTML 초기 로드하는 동안에는 브라우저가 자동으로 처리하기 때문에 크게 신경 쓸 필요가 없습니다. 스트리밍 하는 동안 HTML 청크를 받으면, 브라우저는 그 콘텐츠를 출력합니다. 서버에서 스트리밍을 활성화하려면 헤더를 조정해야 합니다. 아래는 예시입니다. 그리고 응답에서는 ReadableStream을 사용합니다. 이것은 Bun을 사용한 예시입니다. enqueue 안에 있는 각 문자열은 브라우저가 받게 될 청크입니다. 스트리밍 중 HTML 콘텐츠 변경 성능상의 이점이 많기 때문에 많이 사용되는 방법 중 하나는 스트리밍 되는 동안 HTML 콘…

March 21, 2024
translate
react
(번역) 접근성을 고려하여 CSS 작성하기

원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939 CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다. 이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다. 읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다. 약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리…

February 18, 2024
translate
a11y
(번역) 리액트 서버 컴포넌트를 위한 스토리북

원문: https://storybook.js.org/blog/storybook-react-server-components/ 스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용 리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다. 가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다. 이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 …

February 02, 2024
translate
react
(번역) 2024년 자바스크립트의 5가지 가장 혁신적인 제안

원문: https://javascript.plainenglish.io/the-5-most-innovative-proposals-for-javascript-planned-for-2024-22139dd2f546 JavaScript Logo 2024 자바스크립트가 빠른 속도로 발전함에 따라 2024년은 이 프로그래밍 언어에 있어 중요한 해가 될 것입니다. 이 글에서는 개발자들이 자바스크립트 코딩에 접근하는 방식을 재정의하기 위한 최신 제안을 자세히 살펴보겠습니다. 이 글이 유용하길 바랍니다! 시작해 봅시다! 목차 데코레이터 Temporal API 파이프라인 연산자 Error cause 레코드와 튜플 1. 데코레이터 자바스크립트에 데코레이터가 도입된 것은 개발자가 클래스, 메서드, 프로퍼티 및 매개변수의 동작을 조작하고 개선할 수 있는 방법이 크게 발전했음을 의미합니다. 파이썬과 타입스크립트와 같은 다른 프로그래밍 언어에서 유래한 데코레이터는 기능을 추가하거나 수정하는 간결하고 선언적인…

December 20, 2023
translate
javascript
(번역) 시그널(Signal)에 대한 소개

원문: https://preactjs.com/blog/introducing-signals/ 시그널은 앱이 복잡해져도 빠른 속도를 유지하도록 하는 상태 표현 방식입니다. 시그널은 반응형 원칙에 기반을 두고 있으며, 가상 돔에 최적화된 독특한 구현을 통해 개발자에게 훌륭한 경험을 제공합니다. 본질적으로 시그널은 특정 값을 가지고 있는 .value 속성을 가진 객체입니다. 컴포넌트 내에서 시그널의 value 속성에 접근하면, 그 시그널의 값이 변경될 때 해당 컴포넌트가 자동으로 업데이트됩니다. 이는 간단하고 작성하기 쉬울 뿐만 아니라, 앱이 얼마나 많은 컴포넌트를 가지고 있든 상태 업데이트가 빠르게 유지되도록 보장합니다. 시그널은 기본적으로 빠르며, 백그라운드에서 자동으로 업데이트를 최적화해줍니다. REPL에서 실행 시그널은 훅과 달리 컴포넌트 내부 또는 외부에서 사용할 수 있습니다. 또한 시그널은 훅과 클래스 컴포넌트 모두에서 훌륭하게 작동하므로, 기존 지식을 활용하며 자신의 속도에 …

November 27, 2023
translate
react
(번역) React에서 UI와 로직 분리하기:헤드리스 컴포넌트를 사용한 클린 코드 접근법

원글: https://itnext.io/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components-82e46b5820c 프런트엔드 개발 영역에서는 용어와 패러다임이 때로는 이해하기 어려울 수 있으며 ‘헤드리스 UI’ 또는 ‘헤드리스 컴포넌트’도 이 범주에 속할 수 있습니다. 이러한 용어들이 무엇을 의미하는지 궁금해서 고개를 갸웃거리고, 혼자만 그런 것이 아닙니다. 사실, 혼란스러운 이름에도 불구하고 이러한 개념들은 복잡한 사용자 인터페이스 관리를 상당히 단순화할 수 있는 매력적인 전략입니다. 헤드리스 컴포넌트는 난해해 보일 수 있지만, 그 진정한 힘은 유연성, 재사용 가능성, 그리고 코드베이스의 구성과 깔끔함을 향상시킬 수 있는 능력에 있습니다. 이 글에서는 이 패턴이 정확히 무엇인지, 왜 유용한지, 그리고 인터페이스 디자인에 대한 접근 방식을 어떻게 혁신할 수 있는지에 대해 탐구해 볼 것입니다. …

September 13, 2023
translate
react
(번역) 우리들을 위한 디자인 패턴

원글: https://github.com/kamranahmedse/design-patterns-for-humans/blob/master/readme.md 역자주: 이 글은 Kamran Ahmed의 글이며, PHP-7 기준으로 작성된 예제 코드를 타입스크립트로 변경하였습니다. logo 🎉 디자인 패턴에 대한 초간단 설명입니다! 🎉 누구든지 혼란스럽게 만들 수 있는 주제입니다. 이 글에서는 최대한 간단한 방식으로 설명함으로써 당신의 기억 속 (그리고 아마도 제 기억)에 각인시키기 위해 노력할 것입니다. 저의 다른 프로젝트도 확인하고 트위터에서 “안녕”이라고 인사해 보세요. 생성 패턴 구조 패턴 행동 패턴 심플 팩토리(Simple Factory) 어댑터(Adapter) 책임 연쇄(Chain of Responsibility) 팩토리 메서드(Factory Method) 브릿지(Bridge) 명령(Command) 추상 팩토리(Abstract Factory) 컴포지트(Composite) 반복자…

August 27, 2023
translate
cs
(번역) React Query를 사용하여 서버 상태를 관리하는 방법

원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …

June 21, 2023
translate
react
react-query
(번역) 캐시 시스템 설계할 때 기억해야 할 6가지 캐싱 전략

원글: https://javascript.plainenglish.io/6-caching-strategies-to-remember-while-designing-cache-system-da058a3757cf 캐시 시스템 관련 용어, 읽기 vs 쓰기 중심의 애플리케이션에서 캐싱 전략, 캐시를 무효화하는 방법 등에 대해서 알려드리겠습니다. 개요 캐싱의 목표는 원본 소스에서 데이터를 가져오는 횟수를 줄여 처리 속도를 높이고 대기 시간을 감소시키는 것입니다. 캐싱은 인메모리 캐싱, 디스크 캐싱, 데이터베이스 캐싱, CDN 캐싱과 같은 다양한 수준의 아키텍처에서 구현될 수 있습니다. 데이터는 각각 고유한 이점이 있는 다양한 기술을 사용하여 캐싱할 수 있습니다. 인메모리 캐싱은 컴퓨터의 주 메모리에 데이터를 저장하여 디스크 저장소 보다 빠른 액세스를 제공합니다. 반면 디스크 캐싱은 하드 디스크에 데이터를 저장하므로 주 메모리보다는 느리지만 원격 소스에서 데이터를 가져오는 것보다 빠릅니다. 데이터…

June 13, 2023
translate
cs
(번역) StructuredClone API를 사용하여 객체를 깊은 복사하는 법

원문: https://blog.openreplay.com/deep-copying-objects-with-the-structuredclone-api/ Deep Copying Objects with the StructuredClone API 개요: 자바스크립트에서 객체를 복사하는 것은 간단하지 않으며, 이는 잘 알려진 문제입니다. 그러나 이 글에서는 해결책을 제공합니다. StructuredClone API를 사용하면 모든 객체를 간단하고 빠르게 복사할 수 있습니다. 자바스크립트에서 객체가 변수에 저장될 때, 해당 변수는 객체의 참조값을 갖습니다. 이는 변수 자체에 객체를 저장하는 것이 아니라, 객체의 메모리 위치를 나타내는 식별자를 저장한다는 것을 의미합니다. 객체의 복사는 원시 타입과는 다른 방식으로 동작합니다. 얕은 복사(Shallow Copy) vs. 깊은 복사(Deep Copy) 자바스크립트에서 값은 두 가지 방법으로 복사할 수 있습니다. 얕은 복사와 깊은 복사입니다. 얕은 복…

May 07, 2023
translate
javascript
\ No newline at end of file + Posts
(번역) 타입스크립트에서 'As Const' 이해하기

원글: https://www.omarileon.me/blog/typescript-as-const 새로운 구문인 “상수 어설션(const assertions)“은 TypeScript 3.4에서 도입되었습니다. 이것은 변수가 변경되지 않을 것(immutable)이고 가능한 엄격한 타입을 제공해야 한다는 것을 타입스크립트에 알려줍니다. 이는 타입의 종류에 따라 다르게 영향을 미치므로, 이 글에서는 상수 어설션을 어떻게 사용하고 왜 유용한지에 대해 설명할 것입니다. 문자열/숫자 문자열이나 숫자에 “as const”를 추가하면 특정 값으로 타입을 좁힐 수 있습니다. 숫자의 경우 문자열 또는 숫자의 경우 일반적으로 “const”를 사용하여 변수를 정의하면 동일한 효과를 얻을 수 있기 때문에 덜 유용합니다. 런타임 안전성이라는 추가 이점도 있습니다. 때로는 변수로 값을 정의하지 않고, 단순히 문자열 리터럴을 사용하고 싶을 수 있습니다. 예를 들어 값을 반환할 때처럼요. 그때 “as const”…

April 14, 2024
translate
typescript
(번역) 스트리밍 HTML과 DOM 비교 알고리즘

원글: https://aralroca.com/blog/html-node-streaming 썸네일 최근 몇 년간 브라우저는 HTML과 자바스크립트를 스트리밍을 지원하기 시작했습니다. 이 글에서는 이에 대한 장점과 브라우저가 자동으로 수행하지 않는 다른 작업들을 통해 스트리밍의 이점을 최대한 활용할 수 있는 방법에 대해 이야기할 것입니다. 스트리밍 HTML 초기 로드하는 동안에는 브라우저가 자동으로 처리하기 때문에 크게 신경 쓸 필요가 없습니다. 스트리밍 하는 동안 HTML 청크를 받으면, 브라우저는 그 콘텐츠를 출력합니다. 서버에서 스트리밍을 활성화하려면 헤더를 조정해야 합니다. 아래는 예시입니다. 그리고 응답에서는 ReadableStream을 사용합니다. 이것은 Bun을 사용한 예시입니다. enqueue 안에 있는 각 문자열은 브라우저가 받게 될 청크입니다. 스트리밍 중 HTML 콘텐츠 변경 성능상의 이점이 많기 때문에 많이 사용되는 방법 중 하나는 스트리밍 되는 동안 HTML 콘…

March 21, 2024
translate
react
(번역) 접근성을 고려하여 CSS 작성하기

원글: https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939 CSS를 사용하여 웹사이트와 앱의 접근성을 향상시키는 데 도움이 되는 팁에 대한 소개입니다. 이 글은 러시아어(역자: Workafrolic), 포르투갈어(역자: Maujor), 그리고 일본어(역자: Keita Nakanishi)로 번역되었습니다. 읽기를 선호하지 않는다면, CSS Conf Budapest에서 이 글의 대부분에 대해 이야기한 녹음본을 들을 수 있습니다. 약 1년 전, 저는 웹 접근성에 좀 더 집중하기 시작했습니다. 저에게 가장 효과적인 학습 방식은 다른 사람들을 가르치는 것입니다. 이것이 바로 제가 밋업과 컨퍼런스에서 발표하고, 이 주제에 대한 글을 쓰는 이유 중 하나입니다. 저는 Smashing Magazine에 점진적인 향상, 그리고 접근성 기초에 대해 Medium에 글을 작성했습니다. 이 글은 접근성 팁 모음 시리…

February 18, 2024
translate
a11y
(번역) 리액트 서버 컴포넌트를 위한 스토리북

원문: https://storybook.js.org/blog/storybook-react-server-components/ 스토리북 8.0 알파 업그레이드를 통한 스토리북에서 RSC 사용 리액트 서버 컴포넌트 (RSC)는 리액트 기반 웹 UI의 새로운 프로그래밍 모델입니다. 기존의 전통적인 리액트 “클라이언트” 컴포넌트와 달리, 서버에서만 렌더링됩니다. 이는 다양한 성능 및 보안 이점을 가져오지만, 기존의 리액트 도구 및 라이브러리와는 크게 다릅니다. 가장 크게 영향을 받는 부분 중 하나는 컴포넌트 기반 개발 및 테스트입니다. 스토리북, 테스팅 라이브러리 및 Playwright/Cypress와 같은 컴포넌트 테스팅과 같은 도구들은 사용자의 컴포넌트가 브라우저 (또는 JSDom)에서 렌더링되고 있다고 가정합니다. 그러나 서버 컴포넌트의 경우, 이러한 가정이 성립하지 않습니다. 이로 인해, 서버를 위한 격리된 컴포넌트 개발 및 테스트를 수행하는 것이 무엇을 의미하는가? 라는 의문이 …

February 02, 2024
translate
react
(번역) 2024년 자바스크립트의 5가지 가장 혁신적인 제안

원문: https://javascript.plainenglish.io/the-5-most-innovative-proposals-for-javascript-planned-for-2024-22139dd2f546 JavaScript Logo 2024 자바스크립트가 빠른 속도로 발전함에 따라 2024년은 이 프로그래밍 언어에 있어 중요한 해가 될 것입니다. 이 글에서는 개발자들이 자바스크립트 코딩에 접근하는 방식을 재정의하기 위한 최신 제안을 자세히 살펴보겠습니다. 이 글이 유용하길 바랍니다! 시작해 봅시다! 목차 데코레이터 Temporal API 파이프라인 연산자 Error cause 레코드와 튜플 1. 데코레이터 자바스크립트에 데코레이터가 도입된 것은 개발자가 클래스, 메서드, 프로퍼티 및 매개변수의 동작을 조작하고 개선할 수 있는 방법이 크게 발전했음을 의미합니다. 파이썬과 타입스크립트와 같은 다른 프로그래밍 언어에서 유래한 데코레이터는 기능을 추가하거나 수정하는 간결하고 선언적인…

December 20, 2023
translate
javascript
(번역) 시그널(Signal)에 대한 소개

원문: https://preactjs.com/blog/introducing-signals/ 시그널은 앱이 복잡해져도 빠른 속도를 유지하도록 하는 상태 표현 방식입니다. 시그널은 반응형 원칙에 기반을 두고 있으며, 가상 돔에 최적화된 독특한 구현을 통해 개발자에게 훌륭한 경험을 제공합니다. 본질적으로 시그널은 특정 값을 가지고 있는 .value 속성을 가진 객체입니다. 컴포넌트 내에서 시그널의 value 속성에 접근하면, 그 시그널의 값이 변경될 때 해당 컴포넌트가 자동으로 업데이트됩니다. 이는 간단하고 작성하기 쉬울 뿐만 아니라, 앱이 얼마나 많은 컴포넌트를 가지고 있든 상태 업데이트가 빠르게 유지되도록 보장합니다. 시그널은 기본적으로 빠르며, 백그라운드에서 자동으로 업데이트를 최적화해줍니다. REPL에서 실행 시그널은 훅과 달리 컴포넌트 내부 또는 외부에서 사용할 수 있습니다. 또한 시그널은 훅과 클래스 컴포넌트 모두에서 훌륭하게 작동하므로, 기존 지식을 활용하며 자신의 속도에 …

November 27, 2023
translate
react
(번역) React에서 UI와 로직 분리하기:헤드리스 컴포넌트를 사용한 클린 코드 접근법

원글: https://itnext.io/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components-82e46b5820c 프런트엔드 개발 영역에서는 용어와 패러다임이 때로는 이해하기 어려울 수 있으며 ‘헤드리스 UI’ 또는 ‘헤드리스 컴포넌트’도 이 범주에 속할 수 있습니다. 이러한 용어들이 무엇을 의미하는지 궁금해서 고개를 갸웃거리고, 혼자만 그런 것이 아닙니다. 사실, 혼란스러운 이름에도 불구하고 이러한 개념들은 복잡한 사용자 인터페이스 관리를 상당히 단순화할 수 있는 매력적인 전략입니다. 헤드리스 컴포넌트는 난해해 보일 수 있지만, 그 진정한 힘은 유연성, 재사용 가능성, 그리고 코드베이스의 구성과 깔끔함을 향상시킬 수 있는 능력에 있습니다. 이 글에서는 이 패턴이 정확히 무엇인지, 왜 유용한지, 그리고 인터페이스 디자인에 대한 접근 방식을 어떻게 혁신할 수 있는지에 대해 탐구해 볼 것입니다. …

September 13, 2023
translate
react
(번역) 우리들을 위한 디자인 패턴

원글: https://github.com/kamranahmedse/design-patterns-for-humans/blob/master/readme.md 역자주: 이 글은 Kamran Ahmed의 글이며, PHP-7 기준으로 작성된 예제 코드를 타입스크립트로 변경하였습니다. logo 🎉 디자인 패턴에 대한 초간단 설명입니다! 🎉 누구든지 혼란스럽게 만들 수 있는 주제입니다. 이 글에서는 최대한 간단한 방식으로 설명함으로써 당신의 기억 속 (그리고 아마도 제 기억)에 각인시키기 위해 노력할 것입니다. 저의 다른 프로젝트도 확인하고 트위터에서 “안녕”이라고 인사해 보세요. 생성 패턴 구조 패턴 행동 패턴 심플 팩토리(Simple Factory) 어댑터(Adapter) 책임 연쇄(Chain of Responsibility) 팩토리 메서드(Factory Method) 브릿지(Bridge) 명령(Command) 추상 팩토리(Abstract Factory) 컴포지트(Composite) 반복자…

August 27, 2023
translate
cs
(번역) React Query를 사용하여 서버 상태를 관리하는 방법

원문: https://www.tecforfun.com/frameworks/how-to-manage-server-state-with-react-query/ React Query는 React 애플리케이션에서 데이터 페칭과 캐싱 프로세스를 간소화하는 라이브러리입니다. 이 라이브러리는 API와 기타 데이터 소스로부터 데이터를 페칭하고 업데이트하는 데 필요한 도구와 유틸리티를 제공하며 데이터 페칭의 상태와 캐싱을 자동으로 관리합니다. 이 라이브러리는 React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있도록 다양한 훅과 유틸리티를 제공합니다. 이 포스트에서는 React Query의 주요 기능에 대해 이야기하겠습니다. 여기서 제 목적은 가능한 한 빨리 React Query를 사용하여 작업을 시작할 수 있는 출발점을 제공하는 것입니다. React 앱 개발에 경험이 있다면, 클라이언트 상태 관리를 위해 Redux와 같은 라이브러리를 사용한 적이 있을 수 있습니다. 반면에, React Query는 …

June 21, 2023
translate
react
react-query
(번역) 캐시 시스템 설계할 때 기억해야 할 6가지 캐싱 전략

원글: https://javascript.plainenglish.io/6-caching-strategies-to-remember-while-designing-cache-system-da058a3757cf 캐시 시스템 관련 용어, 읽기 vs 쓰기 중심의 애플리케이션에서 캐싱 전략, 캐시를 무효화하는 방법 등에 대해서 알려드리겠습니다. 개요 캐싱의 목표는 원본 소스에서 데이터를 가져오는 횟수를 줄여 처리 속도를 높이고 대기 시간을 감소시키는 것입니다. 캐싱은 인메모리 캐싱, 디스크 캐싱, 데이터베이스 캐싱, CDN 캐싱과 같은 다양한 수준의 아키텍처에서 구현될 수 있습니다. 데이터는 각각 고유한 이점이 있는 다양한 기술을 사용하여 캐싱할 수 있습니다. 인메모리 캐싱은 컴퓨터의 주 메모리에 데이터를 저장하여 디스크 저장소 보다 빠른 액세스를 제공합니다. 반면 디스크 캐싱은 하드 디스크에 데이터를 저장하므로 주 메모리보다는 느리지만 원격 소스에서 데이터를 가져오는 것보다 빠릅니다. 데이터…

June 13, 2023
translate
cs
(번역) StructuredClone API를 사용하여 객체를 깊은 복사하는 법

원문: https://blog.openreplay.com/deep-copying-objects-with-the-structuredclone-api/ Deep Copying Objects with the StructuredClone API 개요: 자바스크립트에서 객체를 복사하는 것은 간단하지 않으며, 이는 잘 알려진 문제입니다. 그러나 이 글에서는 해결책을 제공합니다. StructuredClone API를 사용하면 모든 객체를 간단하고 빠르게 복사할 수 있습니다. 자바스크립트에서 객체가 변수에 저장될 때, 해당 변수는 객체의 참조값을 갖습니다. 이는 변수 자체에 객체를 저장하는 것이 아니라, 객체의 메모리 위치를 나타내는 식별자를 저장한다는 것을 의미합니다. 객체의 복사는 원시 타입과는 다른 방식으로 동작합니다. 얕은 복사(Shallow Copy) vs. 깊은 복사(Deep Copy) 자바스크립트에서 값은 두 가지 방법으로 복사할 수 있습니다. 얕은 복사와 깊은 복사입니다. 얕은 복…

May 07, 2023
translate
javascript
\ No newline at end of file diff --git a/posts/troubleshooting/index.html b/posts/troubleshooting/index.html index 11b1b68e..fc400385 100644 --- a/posts/troubleshooting/index.html +++ b/posts/troubleshooting/index.html @@ -66,6 +66,6 @@ } } }) -
React Query useMutation에서 variable 옵셔널하게 사용하기

Introduction #1077 이슈를 보면 mutateFn의 variable을 필수값으로 변경한 이력을 볼 수 있다. mutation인데 왜 variable이 없냐고? 나의 경우에는 URL의 있는 queryParams를 읽어와서 데이터를 넘겨주면 되기 때문에 useSomething hook 안에서 처리하고 싶었다. 문제상황 mutate() 와 같이 variable에 아무 값도 넣지 않고 mutation 함수를 호출 할 수 없다. +

\ No newline at end of file diff --git a/posts/typescript/index.html b/posts/typescript/index.html index 6cad0b53..2a13bc1d 100644 --- a/posts/typescript/index.html +++ b/posts/typescript/index.html @@ -66,4 +66,4 @@ } } }) - Posts
(번역) 타입스크립트에서 'As Const' 이해하기

원글: https://www.omarileon.me/blog/typescript-as-const 새로운 구문인 “상수 어설션(const assertions)“은 TypeScript 3.4에서 도입되었습니다. 이것은 변수가 변경되지 않을 것(immutable)이고 가능한 엄격한 타입을 제공해야 한다는 것을 타입스크립트에 알려줍니다. 이는 타입의 종류에 따라 다르게 영향을 미치므로, 이 글에서는 상수 어설션을 어떻게 사용하고 왜 유용한지에 대해 설명할 것입니다. 문자열/숫자 문자열이나 숫자에 “as const”를 추가하면 특정 값으로 타입을 좁힐 수 있습니다. 숫자의 경우 문자열 또는 숫자의 경우 일반적으로 “const”를 사용하여 변수를 정의하면 동일한 효과를 얻을 수 있기 때문에 덜 유용합니다. 런타임 안전성이라는 추가 이점도 있습니다. 때로는 변수로 값을 정의하지 않고, 단순히 문자열 리터럴을 사용하고 싶을 수 있습니다. 예를 들어 값을 반환할 때처럼요. 그때 “as const”…

April 14, 2024
translate
typescript
타입스크립트 타입 호환성 문제 해결하기 "as const vs satisfies"

Argument of type ‘string’ is not assignable to parameter of type ‘“apple” | “banana”‘.(2345) 타입스크립트 사용시 때때로 예상치 못한 타입 호환성 문제에 직면하곤 합니다. 엄격한 타입 시스템을 가지고 있는 타입스크립트로 코드를 작성하다 보면 더욱 이러한 문제와 자주 직면합니다. 하지만 다행히도, 타입스크립트는 이러한 문제들을 해결할 수 있는 강력한 도구들을 제공합니다. 이 글에서는 타입 호환성 문제를 해결하는 데 있어 as const와 satisfies라는 두 가지 도구의 사용 방법과 각각의 장점에 대해 알아보겠습니다. 먼저 우리가 자주 마주할 수 있는 상황을 예시 코드로 살펴보겠습니다. 위에 에러 메시지는 함수의 인자로 예상되는 타입과 실제로 전달된 타입이 일치하지 않을 때 발생합니다. 이 경우, printPerson 함수는 "apple" 또는 "banana"라는 구체적인 문자열 타입을 기대하지만, perso…

March 31, 2024
typescript
\ No newline at end of file + Posts
(번역) 타입스크립트에서 'As Const' 이해하기

원글: https://www.omarileon.me/blog/typescript-as-const 새로운 구문인 “상수 어설션(const assertions)“은 TypeScript 3.4에서 도입되었습니다. 이것은 변수가 변경되지 않을 것(immutable)이고 가능한 엄격한 타입을 제공해야 한다는 것을 타입스크립트에 알려줍니다. 이는 타입의 종류에 따라 다르게 영향을 미치므로, 이 글에서는 상수 어설션을 어떻게 사용하고 왜 유용한지에 대해 설명할 것입니다. 문자열/숫자 문자열이나 숫자에 “as const”를 추가하면 특정 값으로 타입을 좁힐 수 있습니다. 숫자의 경우 문자열 또는 숫자의 경우 일반적으로 “const”를 사용하여 변수를 정의하면 동일한 효과를 얻을 수 있기 때문에 덜 유용합니다. 런타임 안전성이라는 추가 이점도 있습니다. 때로는 변수로 값을 정의하지 않고, 단순히 문자열 리터럴을 사용하고 싶을 수 있습니다. 예를 들어 값을 반환할 때처럼요. 그때 “as const”…

April 14, 2024
translate
typescript
타입스크립트 타입 호환성 문제 해결하기 "as const vs satisfies"

Argument of type ‘string’ is not assignable to parameter of type ‘“apple” | “banana”‘.(2345) 타입스크립트 사용시 때때로 예상치 못한 타입 호환성 문제에 직면하곤 합니다. 엄격한 타입 시스템을 가지고 있는 타입스크립트로 코드를 작성하다 보면 더욱 이러한 문제와 자주 직면합니다. 하지만 다행히도, 타입스크립트는 이러한 문제들을 해결할 수 있는 강력한 도구들을 제공합니다. 이 글에서는 타입 호환성 문제를 해결하는 데 있어 as const와 satisfies라는 두 가지 도구의 사용 방법과 각각의 장점에 대해 알아보겠습니다. 먼저 우리가 자주 마주할 수 있는 상황을 예시 코드로 살펴보겠습니다. 위에 에러 메시지는 함수의 인자로 예상되는 타입과 실제로 전달된 타입이 일치하지 않을 때 발생합니다. 이 경우, printPerson 함수는 "apple" 또는 "banana"라는 구체적인 문자열 타입을 기대하지만, perso…

March 31, 2024
typescript
\ No newline at end of file diff --git a/react/storybook-react-server-components/index.html b/react/storybook-react-server-components/index.html index b3a598a5..2f88a8f1 100644 --- a/react/storybook-react-server-components/index.html +++ b/react/storybook-react-server-components/index.html @@ -286,4 +286,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/sitemap-pages.xml b/sitemap-pages.xml index 07f099f2..3f96209b 100644 --- a/sitemap-pages.xml +++ b/sitemap-pages.xml @@ -1 +1 @@ -https://soobing.github.io/typescript-eslint-handling-intentionally-ignored-variables2024-10-26T03:29:54.199Zhttps://soobing.github.io/astro-hydration2024-10-26T03:29:54.199Zhttps://soobing.github.io/react/process-env-destructuring-error.md2024-10-26T03:29:54.199Zhttps://soobing.github.io/typescript/typescript-as-const2024-10-26T03:29:54.199Zhttps://soobing.github.io/typescript/as-const-vs-satisfies2024-10-26T03:29:54.199Zhttps://soobing.github.io/react/html-node-streaming2024-10-26T03:29:54.199Zhttps://soobing.github.io/browser/browser-coordinate-size-api2024-10-26T03:29:54.199Zhttps://soobing.github.io/a11y/writing-css-with-accessibility-in-mind2024-10-26T03:29:54.199Zhttps://soobing.github.io/infra/docker-command2024-10-26T03:29:54.199Zhttps://soobing.github.io/react/storybook-react-server-components2024-10-26T03:29:54.199Zhttps://soobing.github.io/react/next-app-router-react-query2024-10-26T03:29:54.199Zhttps://soobing.github.io/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-20242024-10-26T03:29:54.199Zhttps://soobing.github.io/react/server-rendering-and-react-query2024-10-26T03:29:54.199Zhttps://soobing.github.io/react/introducing-signals2024-10-26T03:29:54.199Zhttps://soobing.github.io/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components2024-10-26T03:29:54.199Zhttps://soobing.github.io/cs/design-patterns-for-humans2024-10-26T03:29:54.199Zhttps://soobing.github.io/javascript/garbage-collection2024-10-26T03:29:54.199Zhttps://soobing.github.io/react/How-to-manage-server-state-with-React-Query2024-10-26T03:29:54.199Zhttps://soobing.github.io/cs/6-caching-strategies2024-10-26T03:29:54.199Zhttps://soobing.github.io/javascript/metaprogramming2024-10-26T03:29:54.200Zhttps://soobing.github.io/javascript/deep-copying-objects-with-the-structuredclone-api2024-10-26T03:29:54.200Zhttps://soobing.github.io/feature/input-date2024-10-26T03:29:54.200Zhttps://soobing.github.io/react/recoil-introduction2024-10-26T03:29:54.200Zhttps://soobing.github.io/trouble-shooting/how-to-use-variable-optional-in-useMutation2024-10-26T03:29:54.200Zhttps://soobing.github.io/react/react-query-basic2024-10-26T03:29:54.200Zhttps://soobing.github.io/react/react-state-management2024-10-26T03:29:54.200Zhttps://soobing.github.io/react/error-handle2024-10-26T03:29:54.200Zhttps://soobing.github.io/browser/touch-mouse-event2024-10-26T03:29:54.200Zhttps://soobing.github.io/infra/jenkins-bitbucket2024-10-26T03:29:54.200Zhttps://soobing.github.io/feature/confirm2024-10-26T03:29:54.200Zhttps://soobing.github.io/test/test-introduction2024-10-26T03:29:54.200Zhttps://soobing.github.io/posts2024-10-26T03:29:54.200Zhttps://soobing.github.io/posts/All2024-10-26T03:29:54.200Zhttps://soobing.github.io/posts/framework2024-10-26T03:29:54.200Zhttps://soobing.github.io/posts/react2024-10-26T03:29:54.200Zhttps://soobing.github.io/posts/next2024-10-26T03:29:54.200Zhttps://soobing.github.io/posts/translate2024-10-26T03:29:54.200Zhttps://soobing.github.io/posts/typescript2024-10-26T03:29:54.200Zhttps://soobing.github.io/posts/browser2024-10-26T03:29:54.200Zhttps://soobing.github.io/posts/a11y2024-10-26T03:29:54.200Zhttps://soobing.github.io/posts/infra2024-10-26T03:29:54.200Zhttps://soobing.github.io/posts/react-query2024-10-26T03:29:54.200Zhttps://soobing.github.io/posts/javascript2024-10-26T03:29:54.200Zhttps://soobing.github.io/posts/cs2024-10-26T03:29:54.200Zhttps://soobing.github.io/posts/feature2024-10-26T03:29:54.200Zhttps://soobing.github.io/posts/troubleshooting2024-10-26T03:29:54.200Zhttps://soobing.github.io/posts/test2024-10-26T03:29:54.200Zhttps://soobing.github.io/about2024-10-26T03:29:54.200Zhttps://soobing.github.io/2024-10-26T03:29:54.200Z \ No newline at end of file +https://soobing.github.io/typescript-eslint-handling-intentionally-ignored-variables2024-10-26T03:31:26.480Zhttps://soobing.github.io/astro-hydration2024-10-26T03:31:26.480Zhttps://soobing.github.io/react/process-env-destructuring-error.md2024-10-26T03:31:26.480Zhttps://soobing.github.io/typescript/typescript-as-const2024-10-26T03:31:26.480Zhttps://soobing.github.io/typescript/as-const-vs-satisfies2024-10-26T03:31:26.480Zhttps://soobing.github.io/react/html-node-streaming2024-10-26T03:31:26.480Zhttps://soobing.github.io/browser/browser-coordinate-size-api2024-10-26T03:31:26.480Zhttps://soobing.github.io/infra/docker-command2024-10-26T03:31:26.480Zhttps://soobing.github.io/a11y/writing-css-with-accessibility-in-mind2024-10-26T03:31:26.480Zhttps://soobing.github.io/react/storybook-react-server-components2024-10-26T03:31:26.480Zhttps://soobing.github.io/react/next-app-router-react-query2024-10-26T03:31:26.480Zhttps://soobing.github.io/javascript/the-5-most-innovative-proposals-for-javascript-planned-for-20242024-10-26T03:31:26.480Zhttps://soobing.github.io/react/server-rendering-and-react-query2024-10-26T03:31:26.480Zhttps://soobing.github.io/react/introducing-signals2024-10-26T03:31:26.480Zhttps://soobing.github.io/react/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components2024-10-26T03:31:26.480Zhttps://soobing.github.io/cs/design-patterns-for-humans2024-10-26T03:31:26.480Zhttps://soobing.github.io/javascript/garbage-collection2024-10-26T03:31:26.480Zhttps://soobing.github.io/react/How-to-manage-server-state-with-React-Query2024-10-26T03:31:26.480Zhttps://soobing.github.io/cs/6-caching-strategies2024-10-26T03:31:26.480Zhttps://soobing.github.io/javascript/metaprogramming2024-10-26T03:31:26.480Zhttps://soobing.github.io/javascript/deep-copying-objects-with-the-structuredclone-api2024-10-26T03:31:26.480Zhttps://soobing.github.io/feature/input-date2024-10-26T03:31:26.480Zhttps://soobing.github.io/react/recoil-introduction2024-10-26T03:31:26.480Zhttps://soobing.github.io/trouble-shooting/how-to-use-variable-optional-in-useMutation2024-10-26T03:31:26.480Zhttps://soobing.github.io/react/react-query-basic2024-10-26T03:31:26.480Zhttps://soobing.github.io/react/react-state-management2024-10-26T03:31:26.480Zhttps://soobing.github.io/react/error-handle2024-10-26T03:31:26.480Zhttps://soobing.github.io/browser/touch-mouse-event2024-10-26T03:31:26.480Zhttps://soobing.github.io/infra/jenkins-bitbucket2024-10-26T03:31:26.480Zhttps://soobing.github.io/feature/confirm2024-10-26T03:31:26.480Zhttps://soobing.github.io/test/test-introduction2024-10-26T03:31:26.480Zhttps://soobing.github.io/posts2024-10-26T03:31:26.480Zhttps://soobing.github.io/posts/All2024-10-26T03:31:26.480Zhttps://soobing.github.io/posts/framework2024-10-26T03:31:26.480Zhttps://soobing.github.io/posts/react2024-10-26T03:31:26.480Zhttps://soobing.github.io/posts/next2024-10-26T03:31:26.480Zhttps://soobing.github.io/posts/translate2024-10-26T03:31:26.480Zhttps://soobing.github.io/posts/typescript2024-10-26T03:31:26.480Zhttps://soobing.github.io/posts/browser2024-10-26T03:31:26.480Zhttps://soobing.github.io/posts/infra2024-10-26T03:31:26.480Zhttps://soobing.github.io/posts/a11y2024-10-26T03:31:26.480Zhttps://soobing.github.io/posts/react-query2024-10-26T03:31:26.480Zhttps://soobing.github.io/posts/javascript2024-10-26T03:31:26.480Zhttps://soobing.github.io/posts/cs2024-10-26T03:31:26.481Zhttps://soobing.github.io/posts/feature2024-10-26T03:31:26.481Zhttps://soobing.github.io/posts/troubleshooting2024-10-26T03:31:26.481Zhttps://soobing.github.io/posts/test2024-10-26T03:31:26.481Zhttps://soobing.github.io/about2024-10-26T03:31:26.481Zhttps://soobing.github.io/2024-10-26T03:31:26.481Z \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index 790eafa4..03822378 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1 +1 @@ -https://soobing.github.io/sitemap-pages.xml2024-10-26T03:29:54.228Z \ No newline at end of file +https://soobing.github.io/sitemap-pages.xml2024-10-26T03:31:26.492Z \ No newline at end of file