Posts [React] 리액트 classnames 활용하기 (classnames, !! 연산자)
Post
Cancel

[React] 리액트 classnames 활용하기 (classnames, !! 연산자)


리액트에서 DOM 요소에 class명을 사용하는 법

기본적으로 html에서 div, span 등 DOM 요소들에는 특정 class명을 붙여서 원하는 스타일을 적용할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
<style>
  .box-info{
    text-align: center;
  }
  .mg-10{
    margin: 10px;
  }
</style>
...
<div class="box-info">
</div>

위의 코드는 class명이 box-info인 DOM 요소에 들어가는 텍스트에 가운데(text-align: center) 정렬 스타일이 적용된 상황입니다.

class명은 아래와 같이 여러 개를 붙일 수도 있습니다.

1
2
<div class="box-info mg-10">
</div>

리액트에서는 class를 사용하지 않고 대신 JSX 문법으로 className을 사용합니다.

참고 링크 : https://ko.reactjs.org/docs/introducing-jsx.html

JSX는 HTML보다는 JavaScript에 가깝기 때문에, React DOM은 HTML 어트리뷰트 이름 대신 camelCase 프로퍼티 명명 규칙을 사용합니다. 예를 들어, JSX에서 classclassName가 되고 tabindextabIndex가 됩니다.

그래서 리액트에서는 다음과 같이 className을 사용해서 클래스명을 추가합니다.

1
2
3
4
5
...
return (
  <div className="box-info">
  </div>
);

클래스를 여러 개 추가할 때에도 기존에 html에서 작성하듯이 하면 됩니다.

1
2
3
4
5
...
return (
  <div className="box-info mg-10">
  </div>
)

classnames 모듈 사용하기

이제 소개할 classnames 모듈은 여러 클래스를 추가할 때 뿐만 아니라, 특정 값이 true/false임에 따라 클래스명을 추가하거나, 추가하지 않도록 하는 것을 간단히 구현할 수 있게 해 줍니다.

먼저 npm install classnames 혹은 yarn add classnames 명령어를 입력해 모듈을 설치해 주세요.

classnames : https://www.npmjs.com/package/classnames

예를 들어 다음과 같은 코드가 있다고 가정해 봅니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React from 'react';
import './styles';
import classnames from 'classnames';

const TestComponent = (props) => {

  const {data, description} = props;
  const valid = data.find(item => item.text === 'abc');

  return (
    <div className={classnames('box-info', {mg-10: valid})}>
      {description}
    </div>
  );
}
export default TestComponent;

props로 받아온 data, text값을 활용하여 렌더링하는 TestComponent 컴포넌트 입니다.

valid 에는 find 메소드의 판별식을 만족한 가장 첫 번째 요소를 반환받습니다. 만족하는 값이 없다면 undefined가 저장됩니다.

여기서는 data 객체의 text 프로퍼티에 abc라는 값이 있을 때만 valid에 값이 저장됩니다.

위에서 classnames를 적용한 부분을 보겠습니다.

1
2
3
4
5
return (
  <div className={classnames('box-info', {mg-10: valid})}>
    {description}
  </div>
)

classnames를 활용하여 작성한 형태는 위와 같습니다.

box-info 클래스는 조건없이 기본으로 추가하고, mg-10 클래스는 valid 값이 true로 평가되는 경우에만 추가됩니다.

자바스크립트에서는 undefined, 0(number), ""(string) 들은 boolean 비교 시 false값으로 알아서 간주합니다.

따라서 valid에 undefined가 저장되어 있다면, false로 평가되므로 mg-10 클래스명을 추가하지 않게 됩니다.

undefined가 아닌 특정한 값이 저장되어 있다면 ture로 평가되므로 mg-10 클래스명을 추가합니다.

이렇게 classnames를 활용하면, 위처럼 key(class명) : value 형태로 value가 true 혹은 false로 평가됨에 따라 지정한 스타일을 손쉽게 적용하거나, 적용하지 않을 수 있습니다.


참고사항 - !! 연산자가 권장된 사용법은 아니다

자바스크립트에서는 undefined, 0(number), ""(string) 들을 boolean 비교 시 false값으로 알아서 간주합니다.

그래서 사실 강제로 boolean 타입으로 만드는 !! 연산자가 권장되는 표기법은 아닙니다.

1
2
3
4
5
return (
  <div className={classnames('box-info', {mg-10: !!valid})}>
    {description}
  </div>
)

굳이 위 코드처럼 !!valid로 표현하지 않아도, valid에 undefined가 있었다면 처리할 때 falsy value 이므로 무시되기 때문입니다.

추가적으로 불필요하게 사용되는 예시 중 하나만 언급해 보도록 하겠습니다.

예를 들어 숫자 0인 값이 파라미터로 들어왔다고 가정해 보겠습니다. 개발자는 함수의 파라미터 값이 과연 정의되지 않은 값인지 체크하고 싶었는데, 아래와 같이 코드를 작성했으면 어떨까요?

1
2
3
4
5
function test(param){
  if (!!param) return "success";
  return "fail";
}

if에 들어오는 조건문은 이미 그 자체로 true, false 값이 평가가 됩니다. 하지만 위 코드는 그 부분에 불필요하게 !! 연산자가 사용된 것입니다.

1
2
3
4
5
// (1) 불필요한 !!와 삼항 연산자
{ !!isRender ? <MoreButton/> : null }

// (2) 삼항 연산자와 !!를 제거하고 단순하게 &&로 표현한 모습
{ isRender && <MoreButton/> }

위 코드에서도 !!는 불필요하게 사용된 것이며, 어차피 리액트는 null, undefined, true, false 등의 값은 렌더링하지 않으므로 jsx 코드에서 (1)과 같이 작성하는 것은 불필요합니다.

정리하면 로직상 꼭 !! 연산자를 사용하지 않아도 되는 경우에는, 사용하지 않는 편이 낫다는 것이 많은 사람들의 의견입니다. (반드시 사용하지 말라는 의미는 아닙니다.)

이 외에도 !! 에 대해 불필요하게 사용된 예시를 확인하시려면 아래 링크를 추천드립니다.

참고하면 좋은 링크 : The Double-Bang (!!) Operator And A Misunderstanding Of How JavaScript Handles Truthy / Falsy Values


마지막으로 classnames를 활용하는 예시 케이스들을 정리하면 아래와 같습니다. (꼭 조건을 사용하지 않더라도 여러 개 클래스명을 붙이는 역할을 할 수도 있습니다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
className={classnames('box-info', 'mg-10')} // 'box-info mg-10'

className={classnames(['box-info', 'mg-10'])} // 'box-info mg-10'

className={classnames('box-info', {mg-10 : false})} // 'box-info'

className={classnames('box-info', {mg-10 : null})} // 'box-info'

className={classnames('box-info', {mg-10 : undefined})} // 'box-info'

className={classnames('box-info', {mg-10 : true})} // 'box-info mg-10'

className={classnames('box-info', {mg-10 : 'abc'})} // 'box-info mg-10'

참고사항 : classnames에서 { key : value } 값을 사용할 때 value의 true/false 설명과 관련하여 부정확한 설명이 있어서 일부 수정하였습니다(21.7.29).

VS Code의 유용한 Extension(확장 프로그램) 살펴보기

JavaScript의 spread operator(전개 구문) 사용하기