Web Component로 만들어진 사용자 정의 Tag에 스타일 지정하기

AI에게 물어봤더니 방법은 3가지랜다. (AI의 답변을 이렇게 기록해 둔 이유는 답변을 얻기 위한 과정이 단순히 몇번의 프롬프트 입력을 통해 얻지 않았기 때문이다.)

첫째, 호스트 요소(:host) 스타일로 흔히 하듯 사용자 정의 Tag에 대해 스타일을 지정하면 된다.

즉, 아래처럼.

/* style.css */
my-counter {
  display: block; /* 핵심! */
  margin: 20px;
  border: 1px solid red;
  width: 300px;}

사실, 처음엔 제대로 작동하지 않았다. 그 이유는 display에 대한 스타일 값을 지정하지 않았기 때문이다. 사용자 정의 Tag의 display 스타일에 대한 기본값은 미지정이다. 이 display를 지정하지 않으면 padding 이든 margin이든 width든 모두 엉망진창으로 계산되어 반영된다. 이 첫번째가 내가 원했던 답안이다. 그런데 AI는 2가지를 더 제시해줬고 내가 원했던 이 것보다 더 나은 방안이라며 제시했다. 정말 더 나은 방안인지 확인은 해보지 않았으나 일단 정리해 둔다.

두번째는 CSS 커스텀 프로퍼티의 사용이다.

즉, CSS 변수를 사용하라는 건데… 이 방식이 가장 권장되는 방식이라고 한다. AI가 알려준 것을 나열하면 다음과 같다.

컴포넌트 제작자가 내부 스타일의 특정 값을 CSS 변수(Custom Property)로 노출시켜주면, 사용자는 외부에서 이 변수 값을 바꿔서 내부 스타일을 변경할 수 있습니다. 이것이 가장 유연하고 권장되는 “스타일링 API” 설계 방식입니다.

컴포넌트 제작자 (MyCounter.js): 내부 스타일에서 CSS 변수를 사용합니다. var(–변수명, 기본값) 형태로 작성합니다.

// src/MyCounter.js
const templateStyleMyCounter = document.createElement('template');
templateStyleMyCounter.innerHTML = /*html*/ `
  
 `;

컴포넌트 사용자 (style.css): 외부 CSS에서 해당 변수의 값을 재정의합니다.

/* style.css */
my-counter {
  /* MyCounter 내부의 버튼 배경색을 초록색으로 변경 */
  --my-counter-button-bg: green;
  
  /* MyCounter 내부의 span 글자색을 보라색으로 변경 */
  --my-counter-span-color: purple;
}

세번째는 ::part 의사 요소를 사용하는 방법이라고 한다.

컴포넌트 내부 요소를 직접적인 대상으로 스타일을 지정할 수 있다. AI가 설명해준 내용 그대로를 언급하면 다음과 같다.

컴포넌트 제작자가 내부의 특정 요소에 part 속성을 부여하여 외부에 노출시키면, 사용자는 ::part() 의사요소를 사용해 해당 부분의 스타일을 직접 지정할 수 있습니다.

컴포넌트 제작자 (MyCounter.js): 스타일을 지정할 수 있도록 하고 싶은 내부 요소에 part 속성을 추가합니다.

// src/MyCounter.js
const templateDOMMyCounter = document.createElement('template');
templateDOMMyCounter.innerHTML = /*html*/ `
  <div>
    <span part="count-display"></span>  
    <button id="incrementButton" part="increment-button">Increment</button> 
  </div>
`;

컴포넌트 사용자 (style.css): ::part() 선택자를 사용하여 해당 파트의 스타일을 지정합니다.

/* style.css */
/* my-counter 내부의 increment-button 파트의 스타일 지정 */
my-counter::part(increment-button) {
  background-color: orange;
  border-radius: 0;
}

/* my-counter 내부의 count-display 파트의 스타일 지정 */
my-counter::part(count-display) {
  font-style: italic;
  color: red;
}

과연 미래의 소프트웨어 개발은 AI로 인해 얼마나 그 수준이 업그레이드 될 것이며 개발 복잡도는 얼마나 올라갈까…… 향상된 수준과 복잡도를 AI 없이 오직 사람만으로 수용할수 있을까?

MD (Markdown) 문법

# 제목
## 제목
### 제목
#### 제목
##### 제목
###### 제목

이것은 *기울임*입니다.

이것은 **굵은**이지요.

- 첫번째 항목
  - 첫번째 항목에 대해서..
    - 첫번째 항목에 대해서..
      - 첫번째 항목에 대해서
- 두번째 항목
- 세번째 항목
1. 이것은 첫번째
    1. 이것은 첫번째 항목에..
    2. 이것은 첫번째 항모게...
        1. 이것은..
1. 이것은 두번째

[링크](http://www.gisdeveloper.co.kr)입니다.

![이미지](https://upload.wikimedia.org/wikipedia/commons/4/48/Markdown-mark.svg)

코드를 입력해 볼까요.

`console.log('Hello World')`

```js
function test() {
console.log('Hello world!');
}
```

```rust
fn test() {

}
```

```markdown
> 이 문단은 인용된
> 여러 줄
> 중첩된 인용
```

> test
> 안녕하세요
>> 이중중첩
>>> 삼중중첨
>>>> 사중중첩

---
이것은
***
이것도
___
이것은..

|헤더1|헤더2|헤더3|
|:-|-:|:-:|
|*안녕*|**굿**|~~바이~~|
|엘|젤리|쿠|

이것은 \*표\*입니다.

<span style='color:red'>글의 색상을 넣자</span>
글의 색상을 넣자

안녕하세요.<br> 또는 빈칸2개이상
반가습니다.

안녕하세요.
반가습니다.

Modern JavaScript

구조를 분해해서 할당 (구조분해할당, Destructuring Assignment)

const user = { name: 'Alice', age: 30, city: 'Seoul' };

// 객체 구조 분해
const { name, age } = user;
console.log(name, age); // Alice 30

const { name: name2, ...sss } = user;
console.log(name2, sss); // Alice { age: 30, city: 'Seoul' }

// 다른 이름으로 할당하거나 기본값 설정도 가능
const { name: fullName, email = 'noemail@example.com' } = user;
console.log(fullName, email); // Alice noemail@example.com

// 배열 구조 분해
const colors = ['red', 'green', 'blue'];
const [first, second, third] = colors;
console.log(first, second, third); // red green blue

// 일부만 추출하거나 나머지 요소들을 배열로 받을 수도 있음
const [one, two, ...rest] = colors;
console.log(one, two, rest); // red green ['blue']

구성 요소를 펼쳐 사용하는 문법 (스프레드 문법, Spread Syntax)

/* 배열 결합 및 복사에서의 예 */

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

// 배열 결합
const combinedArr = [...arr1, ...arr2];
console.log(combinedArr); // [1, 2, 3, 4, 5, 6]

// 배열 복사 (얕은 복사)
const copiedArr = [...arr1];
console.log(copiedArr); // [1, 2, 3]

/* 객체 병합 및 복사에서의 예 */

const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };

// 객체 병합
const combinedObj = { ...obj1, ...obj2 };
console.log(combinedObj); // { a: 1, b: 2, c: 3, d: 4 }

// 객체 복사 (얕은 복사)
const copiedObj = { ...obj1 };
console.log(copiedObj); // { a: 1, b: 2 }

/* 함수 인자 전달에서의 예 */

function sum(a, b, c) {
  return a + b + c;
}

const numbers = [1, 2, 3];
console.log(sum(...numbers)); // 6 (배열 요소를 개별 인자로 펼쳐서 전달)

null 또는 undefine이 아닐 경우에 대한 선택적 체이닝(선택적 체이닝, Optional Chaining)

const user = {
  name: 'Dave',
  address: {
    street: '123 Main St',
    city: 'Busan'
  },
  // phone: '010-1234-5678' // phone 속성이 없을 수 있음
};

// 기존 방식 (에러 방지)
let city = user.address && user.address.city;
console.log(city); // Busan

let zipCode;
if (user.address && user.address.zipCode) {
  zipCode = user.address.zipCode;
}
console.log(zipCode); // undefined

// 선택적 체이닝
const userCity = user.address?.city;
console.log(userCity); // Busan

const userPhone = user.phone?.number; // user.phone이 undefined이므로 userPhone은 undefined
console.log(userPhone); // undefined

const userStreet = user.address?.street;
console.log(userStreet); // 123 Main St

// 배열 요소에도 적용 가능
const arr = [{ value: 10 }];
const firstValue = arr?.[0]?.value;
console.log(firstValue); // 10

const emptyArr = [];
const nonExistentValue = emptyArr?.[0]?.value;
console.log(nonExistentValue); // undefined

진짜 null 또는 undefine에 대한 선택 연산자 (Nullish 병합 연산자, Nullish Coalescing Operator)

const userInput = null;
const defaultValue = 'default value';

// || 연산자 (Falsy 값도 걸러냄)
const resultOr = userInput || defaultValue;
console.log(resultOr); // default value

const zeroValue = 0;
const resultOrZero = zeroValue || defaultValue;
console.log(resultOrZero); // default value (0도 Falsy로 간주하여 default value가 할당됨)

// ?? 연산자 (null 또는 undefined만 걸러냄)
const resultNullish = userInput ?? defaultValue;
console.log(resultNullish); // default value

const zeroValueNullish = 0;
const resultNullishZero = zeroValueNullish ?? defaultValue;
console.log(resultNullishZero); // 0 (0은 유효한 값으로 간주됨)

const emptyString = '';
const resultNullishEmptyString = emptyString ?? defaultValue;
console.log(resultNullishEmptyString); // ''

이외에도 화살표 함수, 템플릿 리터럴, 클래스, 모듈, async/await 등이 있음.