들어가며

최근 프로젝트에서 특정 텍스트를 블러 처리해야 하는 요구사항이 있었습니다. 처음에는 단순히 모자이크 효과만 생각했는데, CSS만으로도 여러 가지 흥미로운 방법이 있다는 걸 알게 되었습니다. 이전에는 텍스트를 감싸는 div를 relative로 만들고 그 안에 absolute, z-index 등을 조정한 반투명한 레이어를 올리는 식으로 구현했었는데 다른 방법이 없나 찾아보다가 공부하게 되었습니다.

 text-shadow로 블러 효과 구현하기

가장 먼저 시도해본 방법은 text-shadow와 color: transparent를 조합하는 것이었습니다:

<style>
.blur-text {
  color: transparent;
  text-shadow: 0 0 5px rgba(0,0,0,0.5);
}
</syle>

...

<div class="blur-text">이 텍스트는 블러 처리됩니다.</div>

...

이 방식의 특징은:

  1. 텍스트 자체는 투명하게 처리하고
  2. 그림자 효과로 블러된 모양을 만듭니다
  3. text-shadow의 세 번째 값(blur-radius)을 조절해서 블러 강도를 조절할 수 있습니다

 

 text-shadow의 응용

다른 활용방법은 없나 찾아보다가 예제를 만들어보게 되었습니다.

/* 네온 사인 효과 */
.neon-text {
  color: #fff;
  text-shadow: 
    0 0 5px #fff,
    0 0 10px #fff,
    0 0 20px #0ff,
    0 0 40px #0ff;
}

/* 텍스트 외곽선 효과 */
.outline-text {
  color: white;
  text-shadow: 
    -1px -1px 0 #000,
    1px -1px 0 #000,
    -1px 1px 0 #000,
    1px 1px 0 #000;
}

text-shadow의 값들을 여러 번 지정하면 레이어처럼 쌓이는 효과를 만들 수 있습니다.

네온 사인 효과의 경우:
1. 가장 안쪽부터 바깥쪽으로 점점 커지는 흰색 그림자(0 0 5px #fff, 0 0 10px #fff)로 빛나는 듯한 느낌을 주고
2. 그 위에 하늘색 그림자(0 0 20px #0ff, 0 0 40px #0ff)를 더해 네온 특유의 번짐 효과를 만듭니다

외곽선 효과는 좀 더 재미있는 접근인데요:
1. 텍스트의 상하좌우 네 방향에 1px 크기의 검정 그림자를 배치해서
2. 마치 텍스트에 외곽선을 그린 것처럼 보이게 만듭니다

 

 

 filter: blur 활용하기

/* 기본 블러 */
.blur-element {
  filter: blur(5px);
}

/* 블러와 밝기 조절 */
.blur-and-dark {
  filter: blur(3px) brightness(80%);
}

/* 여러 필터 조합하기 */
.filter-combo {
  filter: blur(2px) brightness(150%) grayscale(50%);
}

filter 속성은 포토샵의 필터처럼 다양한 시각적 효과를 줄 수 있습니다:

기본 블러의 경우:
- blur(5px)는 요소를 마치 핀트가 안 맞는 사진처럼 흐릿하게 만듭니다
- 픽셀값이 클수록 더 흐릿해집니다 블러와 밝기를 조절한 경우:
- blur(3px)로 약간의 흐림 효과를 주고
- brightness(80%)로 전체적인 밝기를 20% 낮춰서
- 마치 서리유리를 통해 보는 것 같은 효과를 만듭니다

필터 조합의 경우:
- blur(2px)로 살짝 흐리게 하고
- brightness(150%)로 밝기를 50% 높이고
- grayscale(50%)로 컬러를 절반만 남겨서
- 마치 빛에 바랜 듯한 효과를 만들어냅니다 이렇게 여러 필터를 조합하면 단순한 블러 이상의 다양한 효과를 만들 수 있습니다.

 예제 통합

<!DOCTYPE html>
<html>
<head>
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
      max-width: 800px;
      margin: 40px auto;
      padding: 0 20px;
    }
    
    .demo-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
      gap: 20px;
      margin: 20px 0;
    }
    
    .demo-item {
      background: #f8f9fa;
      padding: 20px;
      border-radius: 8px;
      text-align: center;
    }
    
    .demo-label {
      font-size: 14px;
      color: #666;
      margin-bottom: 10px;
    }
    
    .blur-text {
      color: transparent;
      text-shadow: 0 0 5px rgba(0,0,0,0.5);
      font-size: 20px;
    }
    
    .neon-text {
      color: #fff;
      text-shadow: 
        0 0 5px #fff,
        0 0 10px #fff,
        0 0 20px #0ff,
        0 0 40px #0ff;
      font-size: 20px;
      background: #333;
      padding: 10px;
    }
    
    .outline-text {
      color: white;
      text-shadow: 
        -1px -1px 0 #000,
        1px -1px 0 #000,
        -1px 1px 0 #000,
        1px 1px 0 #000;
      font-size: 20px;
      background: #f0f0f0;
    }
    
    .blur-element {
      filter: blur(5px);
      font-size: 20px;
    }
    
    .blur-and-dark {
      filter: blur(3px) brightness(80%);
      font-size: 20px;
    }
    
    .filter-combo {
      filter: blur(2px) brightness(150%) grayscale(50%);
      font-size: 20px;
    }
  </style>
</head>
<body>
  <h2>CSS 블러 효과 데모</h2>
  
  <div class="demo-grid">
    <div class="demo-item">
      <div class="demo-label">text-shadow 블러</div>
      <div class="blur-text">안녕하세요</div>
    </div>
    
    <div class="demo-item">
      <div class="demo-label">네온 효과</div>
      <div class="neon-text">네온사인</div>
    </div>
    
    <div class="demo-item">
      <div class="demo-label">외곽선 효과</div>
      <div class="outline-text">외곽선</div>
    </div>
    
    <div class="demo-item">
      <div class="demo-label">filter: blur()</div>
      <div class="blur-element">블러 필터</div>
    </div>
    
    <div class="demo-item">
      <div class="demo-label">blur + brightness</div>
      <div class="blur-and-dark">블러 + 어둡게</div>
    </div>
    
    <div class="demo-item">
      <div class="demo-label">여러 필터 조합</div>
      <div class="filter-combo">필터 조합</div>
    </div>
  </div>
</body>
</html>

 

 실제 구현 결과

생각보다 네온사인은 적절히 사용하면 꽤 괜찮은 상황이 있을 것 같고

개인적인 저의 취향에는 text-shadow 블러를 가볍게 사용하거나 filter blur를 쓸 때는 brightness를 같이 사용하고 싶네요

 

각 효과의 특징과 활용법

실제로 구현해보니 각각의 방식이 장단점이 있었습니다

  1. text-shadow 방식:
    • 텍스트만 블러 처리가 필요할 때 좋습니다
    • 투명도와 그림자 강도를 조절해 다양한 효과를 낼 수 있습니다
    • 네온사인이나 외곽선 같은 특수한 효과도 만들 수 있습니다
  2. filter: blur 방식:
    • 요소 전체에 블러를 주고 싶을 때 좋습니다
      • 이미지에 대한 블러라던지 페이지 자체에 대한 블러처리에선 더 간편하게 줄 수 있습니다.
    • brightness, grayscale 등 다른 필터와 조합이 가능합니다
    • 블러 정도를 픽셀 단위로 정확하게 조절할 수 있습니다

※ 본 포스팅은 개인 기록용으로 반응형 웹에서 이미지나 요소의 비율을 유지하는 방법에 대해 고민하다가 알게된 내용을 정리한 것입니다. 혹시나 저와 비슷한 고민을 하시는 분이 계실까봐 기록으로 남깁니다.

 aspect-ratio로 반응형 웹에서 요소의 비율 유지하기

 

 들어가며

최근 캐러셀(슬라이더) 컴포넌트를 만들면서 이미지의 비율을 어떻게 유지할지 고민이 많았습니다. 처음에는 단순히 width와 height를 픽셀로 고정하거나 퍼센트(%)로 지정했는데, 이게 생각보다 쉽지 않더라구요. 고정 픽셀을 사용하면 모바일에서 깨지고, 퍼센트만 사용하면 부모 요소의 크기에 따라 이미지가 찌그러지는 문제가 있었습니다. div를 여러 개 중첩해서 해결할 수도 있겠지만, 성능 이슈가 걱정되었죠. 그러다 발견한 게 바로 `aspect-ratio` CSS 속성입니다. 

 

 aspect-ratio란?

`aspect-ratio`는 요소의 가로세로 비율을 설정하는 CSS 속성입니다. 예를 들어 16:9 비율을 유지하고 싶다면 다음과 같이 작성할 수 있습니다: 

element { aspect-ratio: 16/9; }

 

실제 사용 예시 - 기존 제가 했던 캐러셀 구현 방식의 문제점

처음에는 이런 식으로 구현했습니다

.carousel-wrapper {
  width: 100%;
  position: relative;
}

.carousel-container {
  width: 100%;
  height: 0;
  padding-bottom: 56.25%; /* 16:9 비율을 위한 padding hack */
  position: relative;
}

.carousel-item {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.carousel-image-wrapper {
  width: 100%;
  height: 100%;
  position: relative;
}

.carousel-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
...

<div class="carousel-wrapper">
  <div class="carousel-container">
    <div class="carousel-item">
      <div class="carousel-image-wrapper">
        <img src="image1.jpg" class="carousel-image" alt="" />
      </div>
    </div>
    <!-- 추가 슬라이드 아이템들... -->
  </div>
</div>

...

여기에서 저는 처음에는 구현이 일단 됐기 때문에 다른 작업을 하다가

실제로 api 연결을 하고 사진이 많아졌을 때 컴포넌트가 버벅거리는 경험을 하게 되었습니다.

그래서 코드를 점검해보고 고민해보는 시간을 갖게 되었고 제가 내렸던 이 방식의 문제점은

  1. div가 너무 많이 중첩됩니다 (wrapper -> container -> item -> image-wrapper -> image)
  2. position: absolute를 사용해야 하는 등 복잡한 position 설정이 필요합니다
  3. padding hack(padding-bottom: 56.25%)을 사용해야 해서 코드가 직관적이지 않습니다
  4. DOM 요소가 많아져서 성능에 영향을 줄 수 있습니다

였고 어떤 식으로 최적화 및 html 요소를 줄일 수 있을지 찾아보았습니다.

 

 aspect-ratio를 활용한 개선된 구현

이걸 aspect-ratio를 사용하면 훨씬 단순화할 수 있었습니다:

.carousel {
  width: 100%;
  overflow: hidden;
}

.carousel-item {
  width: 100%;
  aspect-ratio: 16/9;
}

.carousel-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
<div class="carousel">
  <div class="carousel-item">
    <img src="image1.jpg" class="carousel-image" alt="" />
  </div>
  <!-- 추가 슬라이드 아이템들... -->
</div>

 

단순히 계산해도 원래는
캐러셀 랩퍼 > 컨테이너 > 아이템 > 아이템 랩퍼 순으로 4뎁스나 필요했었는데

개선된 코드에서는 랩퍼 없이 캐러셀, 캐러셀 아이템 만으로 엄청 간소화되었습니다.

더 좋은 방법이 있지 않을까 고민하지 않고 아는 지식만으로 구현하려 했었기 때문에

무지에서 발생한 성능저하였고, 지금 구현은 된다고 하더라도 내가 하는게 최선인지 의심해보던 초심을 몇개월동안 잃어버렸던 것 같은데 포스팅할 좋은 경험이 되었습니다.

 

이런 식으로 가장 바깥의 div에는 item-area를

그 속에는 class="card"가 반복되는 구조를 하고 있는 코드가 있습니다.

여기에서 class="card"에 hover 이벤트로 color=#666 을 줘도 반영되지 않습니다. 

이는 지금 프로젝트에서 기본 값으로 a 태그 자체에

text-decoration: none;
color: black;

을 주었기 때문인데요 이런 경우에는 

.item-area .card:hover + .item-area a{
    color: #666;
}
 

이런 식으로 + 선택자로 같이 지정을 해주면 자연스럽게 원하던 hover 효과를 구현할 수 있었습니다.

 Hover란?

css에서는 스킨처럼 외형을 디자인하는 기능 뿐만 아니라 마우스의 클릭, 요소 위로 올라가는 등의 액션이 있을 때

이벤트를 추가할 수 있습니다.

여기서 말하는 Hover는 마우스를 요소에 이동시켰을 때 어떤 이벤트를 발생시킬지 지정할 수 있게 해주는 역할을 수행합니다.

.news-title{
    color: #000;
    border-bottom: none;
}

.news-title:hover{
    color: blue;
    border-bottom: 1px solid #000;
    cursor: pointer;
    font-weight: 600;
}

위의 css를 적용한 스타일은 

마우스를 올리기 전
마우스를 올린 후

기본 스타일에서는 color: #000;  border-bottom: none; 을 적용해서 글자가 검은색에 밑줄 없음이었는데

마우스를 올리자마자 :hover에서 지정해준 내용대로 글자는 파란색에 밑줄이 잘 동작하는 모습을 볼 수 있습니다.

이외에도  cursor: pointer; 를 이용해서 마우스 포인터를 갖다댔을 때 손모양으로 바꿀수도 있으며

font-weight의 변화를 주어 마우스를 올렸을 때 글자가 더 강조되는 효과를 보일수도 있습니다.

 

 cursor 속성의 종류

※ cursor : option 이런 식으로 사용이 됩니다.

모든 속성을 다 살펴보진 않을 생각이고, 그나마 궁금했던 속성들을 정리해두려고 합니다.

cursor 속성은 꼭 hover와 조합이 되라는 법은 없으며 focus나 이외의 태그에서도 하위속성으로 사용될 수 있습니다.

한가지 주의점은 여기서 커서의 모습만 바꾼다고 해서 그 커서가 해당 역할을 수행하는 것은 아닙니다.

예를 들어 cursur 속성에서 copy를 적용한다고 해도 그에 해당하는 모습으로 커서의 모습을 바꿀뿐이지

그 의미에 해당하는 무엇인가 복사하는 명령은 수행되지 않습니다. 그냥 커서의 모양만 바꾸는 속성입니다.

 

1. alias

마우스커서 우상단에 작은 우상향의 검은 마우스커서를 생성합니다.

2. cell

마우스커서를 두꺼운 십자가 형태로 바꿉니다.

3. copy

마우스커서 우상단에 작은 플러스 표시(+)를 생성합니다.

4.crosshair

마우스커서를 가늘고 긴 십자가 형태로 바꿉니다.

5. grab

마우스커서를 손바닥을 펼친 모양, 장갑 모양으로 바꿉니다.

6. grabbing

마우스커서를 손바닥을 꽉쥔 모양, 주먹 모양으로 바꿉니다.

7. help

마우스커서 우하단에 물음표(?)를 생성합니다.

8. none

마우스커서를 보이지 않게 합니다.

9. not-allowed

마우스커서를 붉은색(red)의 금지 모양으로 바꿉니다.

10. pointer

위의 예제에서 사용했던 속성입니다. 마우스커서를 손가락 하나만 핀 클릭 직전 모양으로 바꿉니다.

11. wait

오른쪽의 이미지가 돌아가는 모습처럼 마우스커서를 바꿉니다.

 

1. [속성]

해당 속성이 있는 요소

[required]

 

2. [속성 = 값]

지정한 속성값이 있는 요소

[target = _blank]

 

3. [속성 ~= 값]

지정한 속성값이 포함된 요소(단어별)

[class ~= button]

 

4. [속성 |= 값]

지정한 속성값이 포함된 요소 (하이픈 포함, 단어별)

[title |= us)

 

5. [속성 ^= 값]

지정한 속성값으로 시작하는 요소

[title ^= eng]

 

6. [속성 $= 값]

지정한 속성값으로 끝나는 요소

[href $= xls]

 

7. [속성 *= 값]

지정한 속성값의 일부가 일치하는 요소

[href *= w3]

다음 내용은 이지스 퍼블리싱의 고경희 님이 집필하신

 

HTML과 CSS 자바스크립트 웹 표준의 정석에서 발췌하였다.

 

 

 

폼에서 사용할 수 있는 태그는 다음과 같다

 

<form>  폼의 시작과 끝을 알려준다.

<fieldset>  폼 요소를 그룹으로 묶는다.

<legend>  필드셋에 제목을 붙인다.

<input>  사용자가 내용을 입력할 필드를 삽입한다. (input 태그는 하위 속성을 많이 가지고 있다.)

<select>, <option> 드롭다운 목록을 삽입한다.

<textarea> 텍스트를 여러 줄 입력할 수 있는 텍스트 영역을 삽입한다. 

<datalist> 데이터 목록을 삽입한다.

 

등이 있다.

 

input 태그는 여러 유형과 속성이 있는데 유형을 먼저 보자면

 

text  한 줄 짜리 텍스트 상자를 생성

password  비밀번호를 입력할 수 있는 필드 생성(비밀번호는 ****와 같이 표시)

search  검색이 가능한 입력 필드를 생성

url  URL주소를 입력할 수 있는 필드 생성

email  이메일주소를 입력할 수 있는 필드 생성

tel  전화번호

checkbox  여러 항목에서 복수 선택이 가능한 체크박스

radio  주어진 여러 항목에서 1개만 선택 가능한 라디오 버튼(원형 버튼을 말한다.)

number  숫자를 조절할 수 있는 스핀 박스

range  숫자를 조절할 수 있는 슬라이드 막대

date  사용자 지역을 기준으로 날짜(연, 월, 일)

month  사용자 지역을 기준으로 날짜(연, 월)

week  사용자 지역을 기준으로 날짜(연, 주)

time  사용자 지역을 기준으로 시간(시, 분, 초, 분할 초)

datetime  국제 표준시로 설정된 날짜와 시간 (연, 월, 일, 시, 분, 초, 분할 초)

datetime-local  사용자가 있는 지역을 기준으로 날짜와 시간 (연, 월, 일, 시, 분, 초, 분할 초)

submit  전송 버튼

reset  리셋 버튼

image  submit 버튼 대신 사용할 이미지

button  일반 버튼 생성

file  파일을 첨부할 수 있는 버튼

hidden  사용자에게는 보이지 않지만 서버로 넘겨주는 값이 있는 필드

 

정도가 있다.

<input> 태그의 유형(type)들은 대부분 그 이름을 보면 그 내용이 유추가 가능하다.

그래서 크게 어렵지는 않고 이 정도가 있다고 보고 넘어가도 좋다

 

여기에서 유형(type)와 아이디(id)는 다르게 입력해야한다.

id는 사용자가 정의한 값으로 할 수 있지만, 어떤 목적으로 만들었는지 알기 쉽게 만들면 좋다.

 

 

 

-

 

<input> 태그는 여러 속성을 가지고 있다.

그 내용들은 다음과 같다

 

autofocus  웹 문서가 열리면 해당 입력 필드 안에 마우스 포인터를 표시(보통 맨 위의 필드에 사용, 예를 들자면 로그인 필드라던지)

placeholder  텍스트 필드에 힌트를 표시 (로그인, 비밀번호 등을 입력하기 전에 회색 글씨로 표시되어 있는게 힌트다.)

readonly  입력 필드를 읽기 전용으로 지정한다. (random이 아니라 read에서 파생된 말이다.)

required  필수 입력 필드를 지정

max  숫자 입력 필드에서 최댓값을 지정

min  숫자 입력 필드에서 최솟값을 지정

step  숫자 입력 필드에서 증감할 간격을 지정

maxlength  텍스트 관련 필드에서 입력할 수 있는 최대 길이

minlength  텍스트 관련 필드에서 입력할 수 있는 최소 길이

size  텍스트 관련 필드에서 화면에 표시할 크기를 지정

list  연결할 데이터 목록을 지정

 

 

그리고 대부분의 label과 input 태그는
<form> 혹은 <fieldset> 안에서 쓰이게 된다

 

예를 들자면

 

<form>

<ul>

    <li>

        <label for "your-tel"> 연락처 </label>

        <input type="tel" id="your-tel">

    </li>

</ul>

</form

 

이런 식이 될 것이다

+ Recent posts