Skip to content

Commit ac23f3c

Browse files
authored
Merge pull request #134 from g1nya2/g1nya2-ui-translate
[UI] CardComponent 구현 -> Whats-next, Get Docker에 적용 [번역] Whats-next 번역글 수정
2 parents da2a605 + 4d2b354 commit ac23f3c

9 files changed

Lines changed: 283 additions & 89 deletions

File tree

public/docs/get-started/get-docker.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,29 @@
1212
>
1313
> 대기업(직원 250명 이상 또는 연간 매출 1000만 달러 이상)에서 Docker Desktop을 상업적으로 사용하려면 [유료 구독](https://www.docker.com/pricing/?_gl=1*h2v28y*_gcl_au*MjczODgxODI4LjE3Mzg0NzA0NDI.*_ga*MjEyODM1MDY2OC4xNzIwMzEyNzQ5*_ga_XJWPQMJYHQ*MTczOTU2MjU3My42MS4xLjE3Mzk1NjI3NjMuNjAuMC4w)이 필요합니다.
1414
15-
<box-component
15+
<div class="not-prose">
16+
<card-component
1617
imgsrc="/imgs/get-started/get-docker-logo/apple_48.svg"
1718
title="Docker Desktop for Mac"
1819
description="macOS 샌드박스 보안 모델을 사용하는 네이티브 애플리케이션으로, Mac에 모든 Docker 도구를 제공합니다."
1920
href="https://docs.docker.com/desktop/setup/install/mac-install/"
20-
></box-component>
21-
22-
23-
<box-component
21+
></card-component>
22+
<card-component
2423
imgsrc="/imgs/get-started/get-docker-logo/windows_48.svg"
2524
title="Docker Desktop for Windows"
2625
description="모든 Docker 도구를 Windows 컴퓨터에 제공하는 네이티브 Windows 애플리케이션입니다."
2726
href="https://docs.docker.com/desktop/setup/install/windows-install/"
28-
></box-component>
29-
30-
31-
<box-component
27+
></card-component>
28+
<card-component
3229
imgsrc="/imgs/get-started/get-docker-logo/linux_48.svg"
3330
title="Docker Desktop for Linux"
3431
description="모든 Docker 도구를 Linux 컴퓨터에 제공하는 네이티브 Linux 애플리케이션입니다."
3532
href="https://docs.docker.com/desktop/setup/install/linux/"
36-
></box-component>
33+
></card-component>
34+
</div>
35+
36+
37+
3738

3839
> **Note**
3940
>

public/docs/get-started/introduction/whats-next.md

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,89 @@
66

77
컨테이너, 이미지, 레지스트리, Docker Compose의 핵심 개념을 배워보세요.
88

9+
<div class="not-prose md:grid-cols-2 lg:grid-cols-3 grid grid-cols-1 gap-4 mb-6 auto-rows-fr">
10+
<card-component
11+
title="What is a container?"
12+
description="첫 번째 컨테이너를 실행하는 방법을 배워보세요."
13+
href="/#/get-started/docker-concepts/the-basics/what-is-a-container"
14+
></card-component>
15+
<card-component
16+
title="What is an image?"
17+
description="이미지 레이어의 기초를 배워보세요."
18+
href="/#/get-started/docker-concepts/the-basics/what-is-an-image"
19+
></card-component>
20+
<card-component
21+
title="What is a registry?"
22+
description="컨테이너 레지스트리에 대해 알아보고, 레지스트리 간 상호 운용성을 살펴본 후, 실제로 레지스트리와 상호작용해 보세요."
23+
href="/#/get-started/docker-concepts/the-basics/what-is-a-registry"
24+
></card-component>
25+
<card-component
26+
title="What is Docker Compose?"
27+
description="Docker Compose를 더 잘 이해해 보세요."
28+
href="/#/get-started/docker-concepts/the-basics/what-is-docker-compose"
29+
></card-component>
30+
</div>
31+
932
## Building images
1033

1134
Dockerfiles, 빌드 캐시 및 다단계 빌드를 사용하여 최적화된 컨테이너 이미지를 만들어보세요.
1235

36+
<div class="not-prose md:grid-cols-2 lg:grid-cols-3 grid grid-cols-1 gap-4 mb-6 auto-rows-fr">
37+
<card-component
38+
title="Understanding image layers"
39+
description="컨테이너 이미지 레이어에 대해 배워보세요."
40+
href="/#/get-started/docker-concepts/building-images/understanding-image-layers"
41+
></card-component>
42+
<card-component
43+
title="Writing a Dockerfile"
44+
description="Dockerfile을 사용하여 이미지를 생성하는 방법을 배워보세요."
45+
href="/#/get-started/docker-concepts/building-images/writing-a-dockerfile"
46+
></card-component>
47+
<card-component
48+
title="Build, tag and publish an image"
49+
description="Docker Hub나 다른 레지스트리에 이미지를 빌드하고 태그한 뒤 게시하는 방법을 알아보세요."
50+
href="/#/get-started/docker-concepts/building-images/build-tag-and-publish-an-image"
51+
></card-component>
52+
<card-component
53+
title="Using the build cache"
54+
description="빌드 캐시가 무엇인지, 어떤 변경이 캐시를 무효화하는지, 그리고 빌드 캐시를 효과과적으로 활용하는 방법을 배워보세요."
55+
href="/#/get-started/docker-concepts/building-images/using-the-build-cache"
56+
></card-component>
57+
<card-component
58+
title="Multi-stage builds"
59+
description="멀티 스테이지 빌드와 그 이점에 대해 잘 이해해 보세요."
60+
href="/#/get-started/docker-concepts/building-images/multi-stage-builds"
61+
></card-component>
62+
</div>
63+
1364
## Running containers
1465

15-
포트 노출, 기본값 재정의, 데이터 유지, 파일 공유, 멀티 컨테이너 애플리케이션 관리를 위한 필수 기술을 익혀보세요.
66+
포트 노출, 기본값 재정의, 데이터 유지, 파일 공유, 멀티 컨테이너 애플리케이션 관리를 위한 필수 기술을 익혀보세요.
67+
68+
<div class="not-prose md:grid-cols-2 lg:grid-cols-3 grid grid-cols-1 gap-4 mb-6 auto-rows-fr">
69+
<card-component
70+
title="Publishing ports"
71+
description="Docker에서 포트를 게시하고 노출하는 것의 중요성을 이해해 보세요"
72+
href="/#/get-started/docker-concepts/running-containers/publishing-ports"
73+
></card-component>
74+
<card-component
75+
title="Overriding container defaults"
76+
description="<code>docker run</code> 명령어를 사용해 컨테이너의 기본 설정을 재정의하는 방법을 알아보세요."
77+
href="/#/get-started/docker-concepts/running-containers/overriding-container-defaults"
78+
></card-component>
79+
<card-component
80+
title="Persisting container data"
81+
description="Docker에서 데이터 지속성이 왜 중요한지 알아보세요."
82+
href="/#/get-started/docker-concepts/running-containers/persisting-container-data"
83+
></card-component>
84+
<card-component
85+
title="Sharing local files with containers"
86+
description="Docker에서 사용 가능한 다양한 스토리지 옵션과 사용법을 살펴보세요."
87+
href="/#/get-started/docker-concepts/running-containers/sharing-local-files"
88+
></card-component>
89+
<card-component
90+
title="Multi-container applications"
91+
description="다중 컨테이너 애플리케이션의 중요성과 단일 컨테이너 애플리케이션과의 차이점을 알아보세요."
92+
href="/#/get-started/docker-concepts/running-containers/multi-container-applications"
93+
></card-component>
94+
</div>

src/scripts/components/box-component.ts

Lines changed: 0 additions & 41 deletions
This file was deleted.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
class CardComponent extends HTMLElement {
2+
static get observedAttributes() {
3+
return ['imgsrc', 'href', 'title', 'description'];
4+
}
5+
6+
constructor() {
7+
super();
8+
}
9+
10+
attributeChangedCallback() {
11+
this.render();
12+
}
13+
14+
connectedCallback() {
15+
this.render();
16+
}
17+
18+
render() {
19+
const imgSrc = this.getAttribute('imgsrc'); // 없으면 null
20+
const href = this.getAttribute('href') || '#';
21+
const title = this.getAttribute('title') || '';
22+
const description = this.getAttribute('description') || '';
23+
24+
this.innerHTML = `
25+
<div class="card">
26+
<a href="${href}" class="card-link">
27+
${
28+
imgSrc
29+
? `<div class="card-icon">
30+
<img class="card-img" src="${imgSrc}" alt="${title}" />
31+
</div>`
32+
: ''
33+
}
34+
<div class="card-content">
35+
<p class="card-description">
36+
<strong class="card-title">${title}</strong><br />
37+
${description}
38+
</p>
39+
</div>
40+
</a>
41+
</div>
42+
`;
43+
}
44+
}
45+
46+
customElements.define('card-component', CardComponent);

src/scripts/components/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import './footer-component';
22
import './header-component';
3-
import './box-component';
3+
import './card-component';
44
import './button-component';

src/scripts/load_md.ts

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,73 @@
11
import { marked } from 'marked';
22

3-
// marked 옵션 설정 (브레이크, GFM 지원 등)
3+
// marked 옵션 설정 (브레이크, GFM 지원 등)
44
marked.setOptions({
55
gfm: true,
66
breaks: true,
77
});
88

9+
// card-component를 블록 태그 및 셀프 클로징 태그로 처리하는 커스텀 토크나이저 추가
10+
// 템플릿 리터럴에서 역참조(\1) 사용 불가하므로 정규식 리터럴로 하드코딩
11+
const blockTagRegex =
12+
/^<(card-component)([\s\S]*?)(?:>([\s\S]*?)<\/card-component>|\s*\/)>/i;
13+
14+
const customBlockTokenizer = {
15+
name: 'custom-block-tag',
16+
level: 'block',
17+
start(src: string) {
18+
return src.match(blockTagRegex)?.index;
19+
},
20+
tokenizer(src: string) {
21+
const match = blockTagRegex.exec(src);
22+
if (match) {
23+
return {
24+
type: 'html',
25+
raw: match[0],
26+
text: match[0],
27+
};
28+
}
29+
return;
30+
},
31+
} as const;
32+
33+
marked.use({ extensions: [customBlockTokenizer] });
34+
935
/**
10-
* mdText를 웹 컴포넌트 태그(<box-component>, <button-component>) 기준으로 분할하여
11-
* 마크다운은 파싱하고, 웹 컴포넌트는 그대로 삽입하는 함수
36+
* 커스텀 파서: <div ...>...</div> 블록을 마크다운 파싱 없이 그대로 삽입
37+
* 나머지 마크다운만 기존 파서로 처리
1238
*/
1339
export async function renderMarkdownWithComponents(
1440
mdText: string,
1541
contentElement: HTMLElement
1642
) {
17-
const tokens = mdText
18-
.split(/(<\/?box-component[^>]*>|<\/?button-component[^>]*>)/gi)
19-
.filter(Boolean);
43+
// <div ...>...</div> 블록 추출 (빈 줄 포함, 중첩 X)
44+
const divBlockRegex = /(<div[\s\S]*?>[\s\S]*?<\/div>)/gi;
45+
const tokens = mdText.split(divBlockRegex).filter(Boolean);
2046

2147
for (const token of tokens) {
22-
if (/^<\/?(box-component|button-component)[^>]*>$/.test(token)) {
48+
if (/^<div[\s\S]*?>[\s\S]*?<\/div>$/.test(token)) {
49+
// div 블록은 그대로 삽입
2350
contentElement.innerHTML += token;
2451
} else if (token.trim()) {
25-
const html = await marked.parse(token);
26-
contentElement.innerHTML += html;
52+
// 나머지는 기존 방식대로 웹 컴포넌트 분리 후 마크다운 파싱
53+
const innerTokens = token
54+
.split(
55+
/(<card-component[\s\S]*?<\/card-component>|<card-component[\s\S]*?\/>|<button-component[\s\S]*?<\/button-component>|<button-component[\s\S]*?\/>)/gi
56+
)
57+
.filter(Boolean);
58+
for (const innerToken of innerTokens) {
59+
if (
60+
/^<\/?(card-component|button-component)[^>]*?>.*?<\/(card-component|button-component)>$/.test(
61+
innerToken
62+
) ||
63+
/^<(card-component|button-component)[^>]*?\/>$/.test(innerToken)
64+
) {
65+
contentElement.innerHTML += innerToken;
66+
} else if (innerToken.trim()) {
67+
const html = await marked.parse(innerToken);
68+
contentElement.innerHTML += html;
69+
}
70+
}
2771
}
2872
}
2973
}

src/scripts/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import '../styles/content_style.css';
33
import '../styles/not_found.css';
44
import '../styles/style.css';
55
import './load_md';
6-
import './components/box-component';
6+
import './components/card-component';
77
import { initializeMarkdownLoader } from './load_md';
88
import { initializeNavFn } from './nav';
99
import { initializeTableContents } from './table-contents';

src/styles/style.css

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
@import 'tailwindcss';
22

3+
@tailwind base;
4+
@tailwind components;
5+
@tailwind utilities;
6+
7+
/* Table 관련 기본 스타일 */
38
@layer base {
9+
card-component {
10+
@apply block;
11+
}
412
table {
513
@apply w-3/4 table-auto;
614
}
@@ -14,3 +22,58 @@
1422
@apply bg-gray-200;
1523
}
1624
}
25+
26+
/* 카드 컴포넌트 스타일 */
27+
@layer components {
28+
.card {
29+
@apply mb-4 flex h-full min-w-0 flex-col rounded-md border border-gray-200 bg-white px-4 py-3 transition duration-200 hover:border-gray-300 hover:shadow;
30+
}
31+
32+
.card-link {
33+
@apply flex items-start gap-4 no-underline;
34+
}
35+
36+
.card-icon {
37+
@apply mt-1 flex-shrink-0;
38+
}
39+
40+
.card-img {
41+
@apply h-6 w-6;
42+
}
43+
44+
.card-content {
45+
@apply flex-1;
46+
}
47+
48+
.card-description {
49+
@apply text-sm leading-snug text-gray-500;
50+
}
51+
52+
.card-title {
53+
@apply text-base font-semibold text-gray-700;
54+
}
55+
}
56+
57+
/* 전역 스타일 */
58+
body {
59+
font-family: 'Noto Sans KR', Arial, sans-serif;
60+
background: #f8fafc;
61+
color: #222;
62+
margin: 0;
63+
padding: 0;
64+
}
65+
66+
h1,
67+
h2,
68+
h3,
69+
h4,
70+
h5,
71+
h6 {
72+
font-weight: 700;
73+
margin-top: 1.5em;
74+
margin-bottom: 0.5em;
75+
}
76+
77+
p {
78+
margin: 0 0 1em 0;
79+
}

0 commit comments

Comments
 (0)