λΆ‰μ€κ±°μœ„ λ…ΈνŠΈ / GoOSe Note

Lucide μ•„μ΄μ½˜ 팩

λ‘₯μ§€
Development
λΆ„λ₯˜
Javascript
등둝일
2024-05-05 03:15:49
쑰회수
424
μ’‹μ•„μš”
0

κ½€ μ˜€λž«λ™μ•ˆ μ•„μ΄μ½˜ νŒ©λ“€μ„ μ°Ύμ•„ν•΄λ§€κ³  μžˆμ—ˆλ‹€.

κΈ°λŠ₯적이면 ν•˜λ‚˜λ„ μ΄μ˜μ§€λ„ μ•Šμ•˜κ³ , 이쁘면 κΈ°λŠ₯적으둜 λΆ€μ‘±ν•˜κΈ° λ•Œλ¬Έμ— μ μ ˆν•œ 아름닀움과 κΈ°λŠ₯성을 κ°–μΆ˜ μ•„μ΄μ½˜ μ•„μ΄μ½˜νŒ©μ„ μ˜€λž«λ™μ•ˆ λΉ„μš©μ„ λ“€μ΄λ©΄μ„œ μ°Ύμ•„ ν•΄λ§€κ³  μžˆμ—ˆλ‹€.
λ§ˆμΉ¨λ‚΄ κ°€μž₯ μ μ ˆν•œκ²ƒμ„ μ°Ύμ•˜λŠ”λ° Feather μ•„μ΄μ½˜νŒ©μ΄λ©° μ—¬νƒœκΉŒμ§€ μ‚¬μš©ν•΄μ˜€κ³  μžˆμ—ˆλ‹€.

μ’…λ₯˜μ—μ„œ ν•œκ³„λ₯Ό 느끼긴 ν–ˆμ§€λ§Œ λ‹€λ₯Έ λŒ€μ•ˆμ΄ μ—†μ—ˆκΈ° λ•Œλ¬Έμ— 계속 μ‚¬μš©ν•΄μ˜€κ³  μžˆμ—ˆλ‹€.
Feather μ•„μ΄μ½˜μ˜ νŠΉμ§•μ€ μ·¨ν–₯을 크게 νƒ€μ§€μ•ŠλŠ”μ κ³Ό μ„ μœΌλ‘œ λ§Œλ“€μ–΄μ Έ μžˆμ–΄μ„œ κ΅΅κΈ°λ₯Ό 자유둭게 λ³€κ²½ κ°€λŠ₯ν–ˆλ‹€. 색 변경은 기본이고 말이닀.

lucide-logo.webp

μ΅œκ·Όμ— Feather μ•„μ΄μ½˜ 베이슀둜 λ§Œλ“€μ–΄μ§„ μ˜€ν”ˆμ†ŒμŠ€ μ•„μ΄μ½˜νŒ© ν”„λ‘œμ νŠΈμΈ Lucideλ₯Ό μ•Œκ²Œ λ˜μ—ˆλ‹€.

https://lucide.dev/

μ’…λ₯˜κ°€ λŒ€λ‹¨νžˆ 많고, 기원과 관리가 λŒ€λ‹¨νžˆ μž˜λ˜μ–΄ μžˆλ‹€λŠ”κ²ƒμ„ λŠλ‚€λ‹€.

λ§Žμ€ ν₯λ―Έκ°€ 생기고 κΌ­ μ‚¬μš©ν•΄λ³΄μžλŠ” λ§ˆμŒμ— ν˜„μž¬ μ‚¬μ΄λ“œ ν”„λ‘œμ νŠΈμ—μ„œ μ‚¬μš©ν•˜κ³  있던 Feather μ•„μ΄μ½˜νŒ©μ—μ„œ κ΅μ²΄ν•΄λ³΄κΈ°λ‘œ ν–ˆλ‹€.

μ μš©ν•΄λ³΄κΈ°

μ΅œκ·Όμ— μ‚¬μ΄λ“œλ‘œ μ˜€ν”ˆμ†ŒμŠ€ ν”„λ‘œμ νŠΈλ₯Ό λ§Œλ“€κ³  μžˆλŠ” λ°”κ΅¬λ‹ˆμ—λ‹€ λ¨Όμ € μ μš©ν•΄λ³΄κ²Œ λ˜μ—ˆλ‹€.

막상 μ‚¬μš©ν•˜λ €κ³  λ¬Έμ„œλ“€μ„ 읽어보고 νŒ¨ν‚€μ§€λ₯Ό μ„€μΉ˜ν•΄λ³΄κ³  μ μš©ν•΄λ³΄λ‹ˆ ν‰μ†Œμ— μ„ ν˜Έν•˜λŠ” λ°©μ‹λŒ€λ‘œ μ‚¬μš©ν•˜λŠ”κ²ƒμ΄ 쉽지 μ•Šμ•˜λ‹€.
기본적으둜 μ‚¬μš©ν•˜λŠ” 방식이 μ»΄ν¬λ„ŒνŠΈλ₯Ό import둜 직접 λΆˆλŸ¬μ™€μ„œ λ°”λ‘œ μ‚¬μš©ν•˜λŠ” 것인데 μ—¬λŸ¬κ°€μ§€ λΆˆνŽΈν•¨κ³Ό μœ μ—°ν•¨μ˜ ν•œκ³„μ˜ 이유둜 μ•„μ΄μ½˜μ„ κ°μ‹ΈλŠ” 래퍼 μ»΄ν¬λ„ŒνŠΈλ₯Ό λ³„λ„λ‘œ λ§Œλ“€μ–΄ μ‚¬μš©ν•˜λŠ” νŽΈμ΄λ‹€.

μ•„μ΄μ½˜ νŒ©μ€ κ°―μˆ˜κ°€ λŒ€λ‹¨νžˆ 많기 λ•Œλ¬Έμ— μ„±λŠ₯에 λŒ€λ‹¨νžˆ λ―Όκ°ν•˜κ²Œ 느끼기 λ•Œλ¬Έμ— μ„±λŠ₯적으둜 λΆ€λ‹΄μ—†λŠ” 선택을 κΌ­ μš°μ„ ν•΄μ•Όν•˜λŠ” μ˜΅μ…˜μ΄λΌμ„œ μ—¬λŸ¬κ°€μ§€λ‘œ κΉŒλ‹€λ‘­λ‹€.

ν˜„μž¬κΉŒμ§€ κ°€μž₯ μ„ ν˜Έν•˜λŠ” μ˜΅μ…˜μ€ λž˜ν•‘ μ»΄ν¬λ„ŒνŠΈμ—μ„œ <svg/>μ—˜λ¦¬λ¨ΌνŠΈλ₯Ό 감싸고 μ•Œλ§Ήμ΄κ°€ λ˜λŠ” svg μ½”λ“œλ“€μ„ μ•„μ΄μ½˜μ„ μ§‘μ–΄λ„£λŠ” 방식을 μ“΄λ‹€. μ½”λ“œλ₯Ό 직접 집어넣기도 ν•˜κ³  # 링크λ₯Ό κ±Έμ–΄ μ—°κ²°μ‹œν‚€κΈ°λ„ ν•œλ‹€.

<svg xmlns="http://www.w3.org/2000/svg">
  {ICON_CODE}
</svg>

이런 ꡬ성을 κ³ μ§‘ν•˜λŠ” μ΄μœ λŠ” μŠ€νƒ€μΌμ˜ μœ μ—°ν•¨ λ•Œλ¬Έμ΄λ‹€.

css varialble둜 μ•„μ΄μ½˜μ˜ μ˜΅μ…˜μ„ μ»΄ν¬λ„ŒνŠΈ propsλ₯Ό ν†΅ν•˜μ§€ μ•Šκ³  μ‘°μ ˆν•  수 μžˆλ‹€.
κ²‰μœΌλ‘œ 보면 μ»΄ν¬λ„ŒνŠΈ props둜 μ‚¬μ΄μ¦ˆλ‚˜ 색같은 μŠ€νƒ€μΌ 값을 μ‘°μ ˆν•˜λŠ”κ²ƒμ΄ νŽΈλ¦¬ν•΄ λ³΄μ΄μ§€λ§Œ κ°’ μ‘°μ ˆμ— κ°€μž₯ 큰 ν•œκ³„λŠ” css media queryλ‹€.
UIλΌλŠ”κ²ƒμ€ λŒ€λ‹¨νžˆ 유기적인 μž₯λ₯΄λΌκ³  λ³Ό 수 μžˆλŠ”λ° cssμ—μ„œ λ„€μ΄ν‹°λΈŒν•˜κ²Œ λŒ€μ‘ν•  수 μžˆλŠ”λ° μžλ°”μŠ€ν¬λ¦½νŠΈκΉŒμ§€ 써가며 λŒ€μ‘ν•˜λŠ”κ²ƒμ€ ꡉμž₯ν•œ μ†μ‹€λ§Œ λΆˆλŸ¬μΌμœΌν‚¨λ‹€.

μ’€ 고민을 ν•˜κ³  μ‚½μ§ˆλμ— λž˜ν•‘ μ»΄ν¬λ„ŒνŠΈλ₯Ό λ§Œλ“€κ²Œ λ˜μ—ˆλ‹€.

vue λž˜ν•‘ μ»΄ν¬λ„ŒνŠΈ

lucide ν”„λ‘œμ νŠΈμ—μ„œ λ”± μ›ν•˜λŠ” ν˜•νƒœλ‘œ λ¦¬μ†ŒμŠ€κ°€ μ œκ³΅λ˜μ–΄μžˆμ§€ μ•Šμ•„μ„œ lucide ν”„λ‘œμ νŠΈμ—μ„œ μ‚¬μš©ν•˜λŠ” μ†ŒμŠ€μ½”λ“œλ“€μ„ λœ―μ–΄λ³΄λ©΄μ„œ μ›ν•˜λŠ” ν˜•νƒœλ‘œ κ°œμ‘°ν•΄λ³΄λ‹ˆ μƒλ‹Ήνžˆ μ›ν•˜λŠ” ν˜•νƒœλ‘œ λ‚˜μ™”λ‹€.

<template>
<svg
  v-if="icon"
  v-html="icon"
  xmlns="http://www.w3.org/2000/svg"
  width="24"
  height="24"
  viewBox="0 0 24 24"
  fill="none"
  stroke="currentColor"
  stroke-width="2"
  stroke-linecap="round"
  stroke-linejoin="round"
  :class="[
    'icon',
    `icon--${props.name}`,
    props.animation && `icon--animation-${props.animation}`,
  ]"
  v-bind="wrapProps"/>
</template>

<script setup>
import { computed } from 'vue'
import { icons } from 'lucide'

const props = defineProps({
  name: String,
  color: String,
  size: String,
  stroke: Number,
  animation: String,
  animationSpeed: String,
})

const icon = computed(() => {
  let src = icons[toPascalCase(props.name)]
  if (!src) return null
  const [ tag, attrs, children ] = src
  const element = createSvgElement(tag, attrs, children)
  return element.innerHTML
})

/**
 * μΉ΄λ©œμΌ€μ΄μŠ€ 문자둜 λ³€κ²½
 * @param {string} str
 * @return {string}
 */
export function toPascalCase(str)
{
  return str.replace(/(\w)(\w*)(_|-|\s*)/g, (g0, g1, g2) => g1.toUpperCase() + g2.toLowerCase())
}

function createSvgElement(tag, attrs, children = [])
{
  const element = document.createElementNS("http://www.w3.org/2000/svg", tag)
  Object.keys(attrs).forEach((name) => {
    element.setAttribute(name, String(attrs[name]))
  })
  if (children.length)
  {
    children.forEach((child) => {
      const childElement = createSvgElement(...child)
      element.appendChild(childElement)
    })
  }
  return element
}

const wrapProps = computed(() => {
  let attr = {
    style: {},
  }
  if (props.color) attr.style['--icon-color'] = props.color
  if (props.size) attr.style['--icon-size'] = props.size
  if (props.stroke) attr.style['--icon-stroke'] = props.stroke
  if (props.animationSpeed) attr.style['--icon-animation-speed'] = props.animationSpeed
  return attr
})
</script>

<style lang="scss" scoped>
.icon {
  display: block;
  margin: var(--icon-margin, 0);
  color: var(--icon-color, var(--color-base));
  width: var(--icon-size, 24px);
  height: var(--icon-size, 24px);
  stroke-width: var(--icon-stroke, 2);
  animation: var(--icon-animation, none);
  transition: var(--icon-transition, none);
  &--animation-rotate {
    --icon-animation: spin var(--icon-animation-speed, 2000ms) linear infinite;
  }
}
</style>

좜처: https://github.com/redgoose-dev/baguni/blob/main/src/components/icons/index.vue

κ΅¬ν˜„μ˜ 핡심은 createSvgElement() ν•¨μˆ˜μ™€ μŠ€νƒ€μΌμ‹œνŠΈ 뢀뢄듀인데 μš”λ Ήκ» λ³€ν˜•μ‹œν‚¬ 수 μžˆλ„λ‘ μ°Έν•˜ν•˜κΈ° μœ„ν•˜μ—¬ μ΄λ ‡κ²Œ λ…ΈνŠΈλ‘œ κΈ°λ‘ν•˜λŠ” 것이닀.
μ•„λ§ˆλ„ svelte 같은것도 μ–ΌμΆ” λΉ„μŠ·ν•˜κ²Œ λ§Œλ“€ 수 μžˆμ„κ²ƒμ΄λ‹€.