Svelte 기본

Shy·2024년 10월 7일

Svelte

목록 보기
7/12

중괄호

Svelte에서 중괄호{}는 템플릿 문법에서 중요한 역할을 한다.

중괄호는 Svelte 컴포넌트의 HTML에서 JS표현식이나 변수 값을 삽입하거나, 조건부 렌더링, 반복문 등을 사용할 때 필요하다.

Svelte는 이러한 중괄호를 통해 데이터를 효율적으로 렌더링한다.

<script>
  let name = 'Svelte';
</script>

<h1>Hello, {name}!</h1>

JS 표현식 사용

중괄호 안에서는 단수 변수뿐만 아니라 JS표현식을 사용할 수 있다.

<script>
  let count = 5;
</script>

<p>{count * 2}</p>  <!-- 출력: 10 -->
<p>{count > 3 ? 'Greater than 3' : 'Less than or equal to 3'}</p> <!-- 출력: Greater than 3 -->

중괄호 안에서는 산술 연산, 조건부 연산 등 다양한 JS표현식이 사용될 수 있다.

조건부 렌더링(if/else)

Svelte는 중괄호와 함께 if/else문법을 사용하여 조건부 렌더링을 할 수 있다.

<script>
  let isLoggedIn = true;
</script>

{#if isLoggedIn}
  <p>Welcome back!</p>
{:else}
  <p>Please log in.</p>
{/if}
  • #if{/if}로 감싼 블록은 조건에 따라 렌더링 된다.

반복문(each블록)

Array like와 같은 것을 순차적으로 출력할 수 있다.

<script>
  let fruits = ['Apple', 'Banana', 'Cherry'];
</script>

<ul>
  {#each fruits as fruit}
    <li>{fruit}</li>
  {/each}
</ul>

이벤트 및 Props 전달

<!-- App.svelte -->
<script>
	export let name;
    let age = 30;

    function incrementAge() {
        age += 1;
    }
</script>

<style>
	h1 {
		color: purple;
	}
</style>

<h1>Hello {name}, my age is {age}!</h1>
<button on:click="{incrementAge()}">Change Age</button>
<!-- main.js -->
import App from './App.svelte';

const app = new App({
	target: document.body,
	props: {
		name: 'world'
	}
});

export default app;

반응형(Reactive)변수 $

컴포넌트의 상태가 변경될 때 자동으로 UI가 업데이트되는 중요한 개념

Svelte는 다른 프레임워크와는 달리 자동으로 상태 변화를 추적하고, 해당 상태와 연결된 UI를 다시 렌더링 한다. 반응형 변수는 크게 두가지로 사용할 수 있는데, 일반 변수와 $:문법을 사용하는 반응형 선언이다.

기본 변수와 반응형 갱신

Svelte는 기본적으로 변수가 변경될 때 이를 추적하여 UI를 자동으로 업데이트한다. 예를 들어, 일반 변수의 값이 변경되면 이를 참조하는 템플릿은 자동으로 다시 렌더링 된다.

<script>
    let count = 0;

    function increment() {
        count += 1;
    }
</script>

<p>Count: {count}</p>
<button on:click={increment}>Increment</button>

count가 변경되면 {count}를 사용한 템플릿 부분이 자동으로 업데이트 된다. 특별한 선언 없이도 Svelte는 count값 변화를 감지하여 UI를 갱신한다.

반응형 선언

특정 변수나 표현식의 값이 변경될 때마다 다른 변수나 로직을 재실행하고 싶을 때는 반응형 선언을 사용한다.

<script>
    let a = 1;
    let b = 2;
    let sum;

    $: sum = a + b; // a 또는 b가 변경될 때마다 sum이 자동으로 갱신됨
</script>

<p>Sum: {sum}</p>
<button on:click={() => a += 1}>Increment A</button>
<button on:click={() => b += 1}>Increment B</button>
  • $: sum = a + b; 선언은 ab가 변경될 때마다 sum을 자동으로 업데이트한다. 이를 통해 sum을 사용하는 템플릿이 다시 렌더링된다.

굳이, 먼저 let이나 const로 선언하지 않아도 반응형 선언을 이용할 수도 있다.

<script>
	export let name;
    let age = 30;

    $: uppercaseName = name.toUpperCase();
    // Svelte의 반응형 시스템 덕분에 별도의 추가 로직 없이도 name 값이 변경될 때마다 uppercaseName이 다시 계산된다.
    
    $: console.log(name);
    // 이름이 바뀔때 출력
    
    $: if (name === 'Maximilian') {
    	age = 31;
    }
    // 이름에 조건에 맞으면 age = 31이 된다.

    function incrementAge() {
        age += 1;
    }

    function changeName() {
        name = 'Maximilan';
    }
</script>

<style>
	h1 {
		color: purple;
	}
</style>

<h1>Hello {uppercaseName}, my age is {age}!</h1>
<button on:click={incrementAge}>Change Age</button>
<button on:click={changeName}>Change Name</button>

<!-- App.svelte -->

위처럼 변수를 업데이트할 뿐만 아니라, 복잡한 로직$:를 사용해 반응형으로 처리할 수 있다.

<script>
    let count = 0;

    // count가 변경될 때마다 이 블록이 실행됨
    $: {
        if (count > 10) {
            console.log("Count is greater than 10");
        }
    }
</script>

<p>{count}</p>
<button on:click={() => count += 1}>Increment</button>
  • count가 변경될 때마다 블록 안의 로직이 실행된다. 여기서 count가 10을 넘으면 콘솔에 로그가 출력된다.

반응형 선언의 중요한 특징

  • 선언의 순서: $:선언은 상단에 있는 변수들이 변경될 때 트리거가 된다. 즉, $:선언 블록은 해당하는 변수의 값이 변경될 때만 실행된다.
  • 반응형 선언 내에서 여러 변수 사용: 한 번에 여러 변수를 관찰하고 싶을 때, 여러 변수를 한 블록 안에 넣을 수 있다.
<script>
    let x = 1;
    let y = 2;

    // x 또는 y가 변경될 때마다 이 블록이 실행됨
    $: console.log(`x: ${x}, y: ${y}`);
</script>

<p>x: {x}, y: {y}</p>
<button on:click={() => x += 1}>Increment X</button>
<button on:click={() => y += 1}>Increment Y</button>

반응형 할당

<script>
    let count = 0;

    function increment() {
        $: count += 1;
    }
</script>

<p>Count: {count}</p>
<button on:click={increment}>Increment</button>

요소(Element) 프로퍼티에 바인딩하기

<!-- App.svelte -->
<script>
    export let name;
    let age = 30;

    $: uppercaseName = name.toUpperCase();
    // Svelte의 반응형 시스템 덕분에 별도의 추가 로직 없이도 name 값이 변경될 때마다 uppercaseName이 다시 계산된다.

    $: console.log(name);
    // 이름이 바뀔때 출력

    $: if (name === 'Maximilian') {
        age = 31;
    }

    function incrementAge() {
        age += 1;
    }

    function changeName() {
        name = 'Maximilian';
    }
</script>

<style>
    h1 {
        color: purple;
    }
</style>

<h1>Hello {uppercaseName}, my age is {age}!</h1>
<button on:click={incrementAge}>Change Age</button>
<!--<button on:click={changeName}>Change Name</button>-->
<input type="text" value="{name}">

위에 input에 글자를 입력해도, name이 바뀌지는 않는다.

기본적으로 Svelte의 데이터 플로우는 단방향으로 위처럼, 선언하면 데이터를 출력하기만 하고 input 요소의 변경 사항에 따라 반응해 name 변수를 바꾸지 않고 있다.

이를 반영하려면 on:input리스너를 추가해야 한다.

<!-- App.svelte -->
<script>
    export let name;
    let age = 30;

    $: uppercaseName = name.toUpperCase();
    // Svelte의 반응형 시스템 덕분에 별도의 추가 로직 없이도 name 값이 변경될 때마다 uppercaseName이 다시 계산된다.

    $: console.log(name);
    // 이름이 바뀔때 출력

    $: if (name === 'Maximilian') {
        age = 31;
    }

    function incrementAge() {
        age += 1;
    }

    function changeName() {
        name = 'Maximilian';
    }

    function nameInput(event) {
        name = event.target.value;
    }
</script>

<style>
    h1 {
        color: purple;
    }
</style>

<h1>Hello {uppercaseName}, my age is {age}!</h1>
<button on:click={incrementAge}>Change Age</button>
<input type="text" value={name} on:input={(e) => nameInput(e)} />

<!--<button on:click={changeName}>Change Name</button>-->

<input type="text" bind:value="{name}"/>

이렇게 값을 bind하면, 양방향 바인드가 된다.

거의 모든 HTML프로퍼티를 바인딩 할 수 있다. 하지만, 너무 남발하지 않는게 좋은데, 단방향 데이터 플로우의 원칙은 이유가 있을 때만 어겨야 한다.

다음은 양방향 바인딩의 단점이다!!

  • 데이터 추적이 어려워짐: 데이터가 UI에서 변경되면 모델에도 자동으로 적용되고, 반대로 모델이 변경되면 UI에도 자동으로 반영된다. 이로 인해 데이터의 변경 원인이 어디에서 비롯되었는지 추적하기 어려워질 수 있다.
  • 데이터 변경의 혼란: 모델과 UI가 서로 영향을 주기 때문에 어느 시점에 데이터가 변경되었는지 예측하기 어렵다. 예를 들어, 여러 컴포넌트에서 동일한 데이터를 양방향 바인딩하면 예상치 못한 충돌이나 변경이 발생할 수 있다.
  • 유지보수성 저하: 양방향 바인딩을 남용하면 코드의 복잡성이 증가하며, 데이터 흐름이 비직관적이 되어 문제가 발생했을 때 해결하기 어려워진다.

on:input & on:change

  • on:input
    • 동작 시점: on:input은 사용자가 입력할 때마다 즉시 트리거된다. 입력 필드의 내용이 변경될 때마다 이벤트가 발생하여, 테긋트가 변경될 때 마다 실시간으로 반응할 수 있다.
    • 사용자가 입력하는 값이 즉시 name변수에 반영되어 Svelte의 반응형 시스템에 의해 화면이 즉시 업데이트된다.
  • on:change
    • 동작 시점: on:change는 사용자가 입력 후 포커스를 벗어나거나(blur), Enter키를 눌러 값을 확정한 시점에 한 번만 트리거된다. 즉, 입력 필드의 내용을 다 작성한 후에 이벤트가 발생한다.
    • 사용자가 입력을 다 끝내야 변수가 업데이트 되므로, 입력 중에는 반영되지 않고, 포커스를 잃거나 다른 입력 장치를 사용할 때만 반응, 즉 최종 값만 반영한다.

여러 컴포넌트 사용하기

<!-- ContactCard.svelte -->
<style>
  .contact-card {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
    max-width: 30rem;
    border-radius: 5px;
    margin: 1rem 0;
  }

  header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 7rem;
  }

  .thumb {
    width: 33%;
    height: 100%;
  }

  img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .user-data {
    width: 67%;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }

  h1 {
    font-size: 1.25rem;
    font-family: "Roboto Slab", sans-serif;
    margin: 0.5rem 0;
  }

  h2 {
    font-size: 1rem;
    font-weight: normal;
    color: #5a5a5a;
    margin: 0;
    margin-bottom: 0.5rem;
  }

  .description {
    border-top: 1px solid #ccc;
    padding: 1rem;
  }
</style>

<div class="contact-card">
  <header>
    <div class="thumb">
      <img src="" alt="" />
    </div>
    <div class="user-data">
      <h1>User Name</h1>
      <h2>Job Title</h2>
    </div>
  </header>
  <div class="description">
    <p>A short description</p>
  </div>
</div>
<!-- App.svelte -->
<script>
    import ContactCard  from "./ContactCard.svelte";
    export let name;
    let age = 30;

    $: uppercaseName = name.toUpperCase();
    // Svelte의 반응형 시스템 덕분에 별도의 추가 로직 없이도 name 값이 변경될 때마다 uppercaseName이 다시 계산된다.

    $: console.log(name);
    // 이름이 바뀔때 출력

    $: if (name === 'Maximilian') {
        age = 31;
    }

    function incrementAge() {
        age += 1;
    }

    function changeName() {
        name = 'Maximilian';
    }

    function nameInput(event) {
        name = event.target.value;
    }
</script>

<style>
    h1 {
        color: purple;
    }
</style>

<h1>Hello {uppercaseName}, my age is {age}!</h1>
<button on:click={incrementAge}>Change Age</button>
<input type="text" bind:value="{name}"/>

<ContactCard/>

<!--<button on:click={changeName}>Change Name</button>-->
<!--<input type="text" value="{name}" on:input={(e) => nameInput(e)} />-->

Props를 통한 통신(부모 -> 자식)

부모 컴포넌트가 자식 컴포넌트에 데이터를 전달할 때 props를 사용한다. Svelte에서 자식 컴포넌트는 export키워드를 사용하여 부모로부터 받은 데이터를 처리할 수 있다.

<!-- App.svelte -->
<script>
    import ContactCard  from "./ContactCard.svelte";
    export let name;
    let title = "";
    let image = "";
    let description = "";
    let age = 30;

    $: uppercaseName = name.toUpperCase();
    // Svelte의 반응형 시스템 덕분에 별도의 추가 로직 없이도 name 값이 변경될 때마다 uppercaseName이 다시 계산된다.

    $: console.log(name);
    // 이름이 바뀔때 출력

    $: if (name === 'Maximilian') {
        age = 31;
    }

    function incrementAge() {
        age += 1;
    }

    function changeName() {
        name = 'Maximilian';
    }

    function nameInput(event) {
        name = event.target.value;
    }
</script>

<style>
    h1 {
        color: purple;
    }
</style>

<h1>Hello {uppercaseName}, my age is {age}!</h1>
<button on:click={incrementAge}>Change Age</button>
<input type="text" bind:value={name}/>
<input type="text" bind:value={title}/>
<input type="text" bind:value={image}/>
<textarea rows="3" bind:value={description}></textarea>

<ContactCard
        userName={name}
        userImage={image}
        jobTitle={title}
        description={description} 
/>

<!--<button on:click={changeName}>Change Name</button>-->
<!--<input type="text" value="{name}" on:input={(e) => nameInput(e)} />-->
<!-- ContactCard -->
<script>
    export let userName;
    export let userImage;
    export let jobTitle;
    export let description;
</script>

<style>
  .contact-card {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
    max-width: 30rem;
    border-radius: 5px;
    margin: 1rem 0;
  }

  header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 7rem;
  }

  .thumb {
    width: 33%;
    height: 100%;
  }

  img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .user-data {
    width: 67%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-left: 1rem;
  }

  h1 {
    font-size: 1.25rem;
    font-family: "Roboto Slab", sans-serif;
    margin: 0.5rem 0;
  }

  h2 {
    font-size: 1rem;
    font-weight: normal;
    color: #5a5a5a;
    margin: 0;
    margin-bottom: 0.5rem;
  }

  .description {
    border-top: 1px solid #ccc;
    padding: 1rem;
  }
</style>
<div class="contact-card">
  <header>
    <div class="thumb">
      <img src={userImage} alt="sss" />
    </div>
    <div class="user-data">
      <h1>{userName}</h1>
      <h2>{jobTitle}</h2>
    </div>
  </header>
  <div class="description">
    <p>{description}</p>
  </div>
</div>

Svelte에서 기본적ㅈ으로 데이터는 단방향으로 흐른다. 부모에서 자식으로 props가 전달되고, 자식은 이를 수정할 수 없다. 자식 컴포넌트는 부모의 데이터를 읽기만 할 수 있으며, 상태를 변경하려면 이벤트를 통해 부모에게 요청해야 한다. 이는 데이터 흐름을 명확하게 하고, 버그를 예방하는 데 도움이 된다.

변수 이름과 props이름이 동일할 경우 간소화

Svelte에서는 props이름과 변수 이름이 동일하면 중복을 줄이기 위해 {description}과 같이 변수 이름만 적어도 된다. Svelte는 이 경우, {description}이라는 변수를 {description}이라는 props로 자동 매핑한다.

<ContactCard
    userName={name}
    userImage={image}
    jobTitle={title}
    {description} 
/>

이벤트 디스패치(자식 -> 부모)

자식 컴포넌트가 부모 컴포넌트에 데이터를 전달할 때는 이벤트 디스패치(dispatch)를 사용한다. 자식 컴포넌트는 이벤트를 발생시키고, 부모 컴포넌트는 그 이벤트를 청취하여 데이터를 처리할 수 있다.

<!-- Parent.svelte -->
<script>
    function handleNameChange(event) {
        alert(`New name: ${event.detail}`);
    }
</script>

<Child on:nameChange={handleNameChange} />
<!-- Child.svelte -->
<script>
    import { createEventDispatcher } from 'svelte';
    const dispatch = createEventDispatcher();

    function changeName() {
        dispatch('nameChange', 'Maximilian');  // 부모 컴포넌트로 'nameChange' 이벤트를 발생시킴
    }
</script>

<button on:click={changeName}>Change Name</button>
  • createEventDispatcher: Svelte는 createEventDispatcher함수를 제공하여 자식 컴포넌트에서 이벤트를 발생시킬 수 있다.
  • 이벤트 발생: dispatch('nameChange', 'Maximilian')에서 nameChange라는 이벤트를 발생시키며, Maximilian이라는 데이터를 부모에게 전달한다.
  • 이벤트 핸들링: 부모 컴포넌트는 on:nameChange를 통해 이 이벤트를 듣고, handleNameChange 함수를 실행하여 데이터를 처리한다.

양방향 바인딩(Optional)

특정 상황에서는 양방향 데이터 바인딩이 필요할 수 있다. Svelte에서는 양방향 바인딩을 허용하긴 하지만, 기본적으로 권장되지는 않는다! 양방향 바인디응ㄴ 자식 컴포넌트에서 부모 컴포넌트의 상태를 직접 변경할 수 있도록 허용한다.

<!-- Parent.svelte -->
<script>
    let name = "Svelte";
</script>

<Child bind:name={name} />
<!-- Child.svelte -->
<script>
    export let name;
</script>

<input type="text" bind:value={name} />

부모 컴포넌트는 bind:name={name}을 통해 양방향 바인딩을 적용한다. 이로 인해 자식 컴포넌트에서 name을 수정하면 부모 컴포넌트의 상태도 실시간으로 업데이트된다.

컨텍스트 API(부모 -> 여러 자식 컴포넌트)

여러 자식 컴포넌트가 동일한 데이터에 접근할 수 있도록 컨텍스트 API을 사용할 수 있다. 이는 여러 계층의 컴포넌트에 걸쳐 데이터를 전달해야 할 때 유용하다.

<!-- Parent.svelte -->
<script>
    import { setContext } from 'svelte';
    const user = { name: "Svelte User", age: 30 };

    setContext('user', user);  // 자식 컴포넌트에 'user' 데이터를 전달
</script>

<Child />
<!-- Child.svelte -->
<script>
    import { getContext } from 'svelte';
    const user = getContext('user');  // 'user' 데이터를 받아옴
</script>

<p>{user.name}, Age: {user.age}</p>
  • setContext: 부모 컴포넌트에서 setContext를 사용해 데이터를 정의하고, 이를 자식 컴포넌트로 전달한다.
  • getContext자식 컴포넌트는 getContext를 사용해 상위 컴포넌트에서 정의된 데이터를 받아 사용할 수 있다.

이 방법은 컴포넌트가 깊게 중첩되어 있을 때 유용하며, props를 통해 데이터를 일일히 전달할 필요 없이 여러 자식 컴포넌트에서 동일한 데이터를 공유할 수 있다.

HTML 컨텐츠 출력하기

XSS 공격

HTML 콘텐츠를 출력할 때는 XSS(크로스 사이트 스크립팅, Cross-Site Scripting) 공격에 취약해질 수 있다.

XSS는 악성 사용자나 공격자가 악성 코드를 웹 페이지에 삽입해, 다른 사용자의 브라우저에서 해당 코드를 실행시키는 공격 방식이다. 이를 통해 공격자는 세션 정보, 쿠키와 같은 데이터를 탈취하거나 웹사이트에 악용할 수 있다.

  • 사용자 세션 탈취: 로그인 정보, 쿠키, 세션 ID 등을 가로챌 수 있다.
  • 피싱 공격: 사용자에게 악성 웹사이트로 리디렉션하는 피싱 공격을 유발할 수 있다.
  • 악성 스크립트 실행: 사용자의 브라우저에서 악성 스크립트를 실행하여 중요한 정보를 노출시킬 수 있다.

Svelte의 XSS공격 방지

Svelte는 기본적으로 안전한 문자열을 렌더링하여 XSS공격을 방지한다. 기본적으로 HTML 요소에 값을 삽입할 때, Svelte는 그 값을 이스케이프 처리한다. 즉, 태그나 스크립트가 포함된 문자열을 자동으로 이스케이프 처리하여 악성 스크립트가 실행되지 않도록 보호한다.

<!-- 안전한 출력 -->
<script>
    let userInput = "<script>alert('XSS Attack!')</script>";
</script>

<p>{userInput}</p>

Svelte는 기본적으로 {userInput}을 HTML로 출력할 때 스크립트를 실행하지 않고, 이를 문자 그대로 이스케이프 처리한다. 즉, <> 같은 특수 문자를 HTML 엔터티로 변환해 브라우저에서 해석되지 않도록 한다.

Svelte에서 {@html}태그를 사용하여 입력을 HTML로 직접 렌더링할 경우, XSS공격에 취약해질 수 있다.

이 태그는 HTML을 문자열로 출력하면서 해당 문자열을 이스케이프 처리하지 않고 그대로 렌더링한다.

<!-- 위험한 경우 -->
<script>
    let userInput = "<script>alert('XSS Attack!')</script>";
</script>

<p>{@html userInput}</p>  <!-- XSS 취약성 발생 -->

위 코드에서 {@html}을 사용하여 사용자 입력을 HTML로 렌더링하고 있는데, 이 경우 악성 스크립트가 실행될 수 있다.

동적 CSS 클래스 설정하기

삼항 연산자를 사용한 동적 클래스 설정

Svelte에서는 템플릿 내에서 삼항 연산자를 사용해 조건에 따라 CSS클래스를 동적으로 설정할 수 있다.

<!-- 조건에 따른 동적 클래스 설정 -->
<script>
  let isActive = true;
</script>

<button class={isActive ? 'active' : 'inactive'}>
  {isActive ? 'Active' : 'Inactive'}
</button>
  • class={isActive ? 'active' : 'inactive'}isActive 값에 따라 active 또는 inactive 클래스를 동적으로 적용한다.
  • 버튼 텍스트도 isActive 상태에 따라 변경된다.

객체를 사용한 클래스 바인딩

객체를 사용하여 여러 클래스를 조건부로 적용할 수 있다.
객체의 키는 클래스 이름, 값은 해당 클래스가 적용될 조건을 나타낸다.

<!-- 객체 기반의 동적 클래스 바인딩 -->
<script>
  let isActive = true;
  let isDisabled = false;
</script>

<button class:active={isActive} class:disabled={isDisabled}>
  Dynamic Button
</button>
  • class:active = {isActive}isActicetrue일 때만 active클래스를 추가한다.
  • class:disabled={isDisabled}isDisabledtrue일 때만 disabled클래스를 추가한다.

객체에 여러 클래스 적용

여러 클래스를 조건부로 적용하려면, 객체를 사용하여 클래스를 동적으로 적용할 수 있다.

객체의 키는 클래스 이름이고, 값은 그 클래스가 적용될 조건이다.

<!-- 여러 클래스 동적 적용 -->
<script>
  let isActive = true;
  let isDisabled = false;
</script>

<button class={ { active: isActive, disabled: isDisabled } }>
  Multiple Dynamic Classes
</button>
  • 이 방식은 클래스가 여러 개일 때 유용하다.
  • activedisabled클래스가 각각 isActiveisDisabled값에 따라 동적으로 적용된다.

클래스 배열 사용(문자열 결합)

여러 클래스를 문자열로 결합하여 동적으로 설정할 수 있다.

<!-- 여러 클래스 문자열 결합 -->
<script>
  let isPrimary = true;
  let isLarge = false;
</script>

<button class={`btn ${isPrimary ? 'btn-primary' : ''} ${isLarge ? 'btn-large' : ''}`}>
  Dynamic Classes
</button>
  • 여러 클래스 명을 문자열로 결합하여 조건에 따라 다르게 적용할 수 있다.
  • btn클래스는 항상 적용되고, btn-primarybtn-large는 조건에 따라 추가된다.

CSS 변수 및 인라인 스타일을 사용한 동적 스타일링

<!-- 동적 스타일 적용 -->
<script>
  let color = 'red';
  let fontSize = 20;
</script>

<button style="color: {color}; font-size: {fontSize}px;">
  Dynamic Styles
</button>

<button on:click={() => color = 'blue'}>
  Change Color to Blue
</button>
  • style속성에서 동적으로 colorfontSize값을 변경할 수 있다.
  • 버튼을 클릭하면 color값이 바뀌어 버튼의 텍스트 색상이 변경된다.

예시

<script>
    export let userName;
    export let userImage;
    export let jobTitle;
    export let description;
</script>

<style>
  .contact-card {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
    max-width: 30rem;
    border-radius: 5px;
    margin: 1rem 0;
  }

  header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 7rem;
  }

  .thumb {
    width: 33%;
    height: 100%;
  }

  .thumb-placeholder {
    background: #cccccc;
  }

  img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .user-data {
    width: 67%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-left: 1rem;
  }

  h1 {
    font-size: 1.25rem;
    font-family: "Roboto Slab", sans-serif;
    margin: 0.5rem 0;
  }

  h2 {
    font-size: 1rem;
    font-weight: normal;
    color: #5a5a5a;
    margin: 0;
    margin-bottom: 0.5rem;
  }

  .description {
    border-top: 1px solid #ccc;
    padding: 1rem;
  }
</style>
<div class="contact-card">
  <header>
    <div class={userImage ? 'thumb' : 'thumb thumb-placeholder'}>
      <img src={userImage} alt="sss" />
    </div>
    <div class="user-data">
      <h1>{userName}</h1>
      <h2>{jobTitle}</h2>
    </div>
  </header>
  <div class="description">
    <p>{description}</p>
  </div>
</div>

userImage를 확인해, 만약 값이 없으면 thumb-placeholder를 적용한다.

profile
신입사원...

0개의 댓글