25년 겨울의 연례행사로써 블로그를 또또또또 엎고 테일윈드에 입문한 후기
서론
25년을 맞이하여 또 블로그를 엎은 것이다. 저번이 몇회차였는지 이제는 기억도 나지 않는다.
아마 4회차였던 것 같은데. 아무튼.
이번 개인 페이지에서는 몇 가지 개선들과 함께 Tailwind 에 입문해봤다:
- UI 를 좀 더 이쁘게!
주변인에게 정갈하다는 말도 들었다 기쁘다 - 메인 페이지에 Discord Rich Presence 와 연동되는 페이지 주인장의 현재 활동 상태를 표시한다.
- 기타 기능 개선 - 게시글 페이지와 발자취 페이지의 우측 네비게이터의 편의성, 게시글 공유 시 스크롤 위치의 대략적 기억 등
이 게시글은 그들에 대한 간단한 후기에 대해 작성하기로 한다.
Discord Rich Presence
기존 페이지에 아래와 같은 메시지가 있었다:
마인크래프트는 개발하는 것도 좋아하지만 같이 플레이하는 것도 좋아합니다! 같이 할 사람 절찬리에 모집 중!(??)
이걸 Now Playing: Minecraft 같은걸로 바꾸자 싶었는데, 사실 마인크래프트를 하루종일 하는건 아니니 그걸로 고정하기엔 살짝 문제가 있어보였다.
그런데 마침 디스코드에 이런 시스템이 이미 있었고, WebSocket 과 IPC 를 통해 이를 적절히 수정할 수 있었던 것이다!
다만 기존 Discord 데스크톱 앱으로 WebStorm, PyCharm 등의 IDE 는 핸들링이 됐으나, 개인적인 바람으로 Procreate 라는 모바일 아이패드 앱도 여기에 떴으면 하는 바람이 있었다.
그리하여, 방에서 잠자는 PC 하나에 Ktor 서버를 올리고, 이 서버가 Discord 와 IPC 로 통신하는 동시에 아이패드와도 Http 를 통해 통신하도록 구현했다.
대략적으로는 아래 사진처럼 된다:

이런걸 왜 피그마로 만들어요?
아이패드의 자동화 동작이 영 정확하지는 않지만, 어차피 표시되는 데이터도 굳이 정확해야 할 이유는 없으므로...
Tailwind 영업맨이야 너?
기존에는 Tailwind 를 싫어했다. 약간 그런 게시글이 있었는데:
어느 날 갑자기
for (int i = 0; i < 8; i++) { ... }를f:i-0;i<8;i++라고 쓰자고 한다면 좋아하는 사람은 아무도 없을 것이다. 왜 굳이 이런 짓을 해야하는가?
솔직히 이 생각에 깊이 공감했다. 멀쩡히 있는 css 를 왜 굳이? 이런 짓을 해야하는거지? 싶은 생각에 가까웠다. 그러나 Tailwind 입문 4일차에 이 생각은 완전히 뒤엎어지게 된다.
앗 들켰네 하지만 들어보세요
정말이지 이것만큼 편한게 없다:
- 단순히 css의 축약이 아니다. 소스를 전체 분석하여 className 으로 인식되는 애들을 추출, css 로 변환하는 애이다.
즉, 커스텀 색상 hex 값이나 너비/높이 수치 등을 얼마든지 넣을 수 있고, 걔네들만 따로 분리해서 css 를 만들어준다. - 매 css 속성이 값의 단위를 포함해 굉장히 짧게 축약된다. 타이핑 부담이 정말 체감이 될 정도로 줄어든다.
기존에 플렉스 컨테이너를 만들기 위해 css 를 작성하면disp까지 적고 Enter,fl까지 적고 Enter 혹은flex모두를 작성했다. 언어 서버가 자동완성을 내려주기 까지의 딜레이를 감안하면 길다.
그러나 Tailwind 를 사용하면flex하나면 된다.padding-right: 4rem;또한p로 축약되며(!),width와height또한w,h로 축약된다. - 단위를 명시적으로 신경쓸 필요가 없다. spacing 과 단위가 이미 내부적으로 rem 을 쓰도록 숨겨져있다.
- 몇몇 속성은 css 로 작성할 때보다 더 직관적인 명명으로 축약되어있다. 예를 들면,
line-clamp-2같은 것들이 있다. 이 속성은 css 로 작성하면 대략 4개의 속성을 적어줘야한다.
게다가, 번들 사이즈도 획기적으로 줄어들 것이다. 기존의 styled 를 그대로 썼다면, 플랙스 컨테이너인 styled 요소가 100개라면 걔네들이 전부 다른 className 으로 해싱되어 스타일 테그에 박히겠지만, 테일윈드를 쓰면 display: flex; 는 style 태그 안에 .flex 라는 클래스로 단 한 번만 작성되고 요소에는 flex 만 작성될 것이다.
외우기 싫다고? 그럼 아래 사진을 보자:

이정도로 친절하게 자동완성을 해준다. 실제로 이게 css 로 뭘로 번역되는지도 보여준다.
솔직히 대강 프로퍼티 이름이나 속성 이름을 치면 어지간해서는 자동완성이 알려준다.
그러면 처음 입문한건데 삽질은 안했냐고 묻는다면 그것은 또 아니다. 몇 가지 삽질을 하기는 했다:
삽질은 안했냐?
첫날은 뭐든 그렇겠지만 머리가 아프다. 새로운 걸 배운다는건 항상 그렇기에. 하지만 WebStorm 의 Tailwind Language Server 와 자동완성과 함께라면 어떻게든 된다.
그러다 슬슬 tailwind 클래스 적는 것에 익숙해진다. flex, flex-col, p-n, relative, 뭐 이런 것들이 이제 자동적으로 나오기 시작한다.
근데? 그렇게 막 적다가 보니 갑자기 이런게 안된다:
const color = someCriteria ? "emerald-400" : "blue-400" return <div className={`bg-${color}`}>...</div>
뭐지? 왜 안되지?
소스 분석 및 번들링
Tailwind 는 빌드 타임에 소스파일에 포함되는 모든 tailwind 클래스명들을 집계하여 '그 클래스만' 번들에 포함시킨다.
예를 들어, 어떤 프로젝트에서 Tailwind 로 'flex' 만 썼다면, tailwind 는 번들링 시 .flex { display: flex; } 만 포함시킨다.
이 말인 즉 모든 클래스명은 빌드타임에 분석이 가능해야한다는 의미가 된다. 위의 예제에서는 런타임에 결정되는 변수를 클래스명에 갖다 넣었으므로 안되는게 맞다.
하지만 대부분의 경우 아래처럼 우회가 된다:
const background = someCriteria ? "bg-emerald-400" : "bg-blue-400" return <div className={background}/>
혹은, tailwind 의 색상 변수가 아닌 것을 넣는다면:
const background = someCriteria ? "bg-[#ff0000]" : "bg-[#0000ff]" return <div className={background}/>
정말 최악의 경우로 뭔지 알 수 없는 색상이 들어온다면 근데 이럴거면 그냥 인라인 스타일을 쓰는게 어떨까:
return ( <div className={"bg-(--arbitrary-color)"} style={{ ["--arbitrary-color" as never]: arbitraryColor }} /> )
props.className 과 twMerge
className 중첩으로 아래와 같은 코드를 만든다:
return (<div className={`flex flex-col ${props.className}`}/>)
위의 코드에는 두 가지 문제가 있는데:
props.className에flex-row가 들어온다고 생각해보자. React 에 너무 오래동안 빠져있던 상태로 생각하면flex-row가flex-col를 대치할것이라고 생각한다.
그러나 물론이지만 그렇지 않다. 아아아주 옛날에className이 여러 개가 올 때 속성이 중복되는 것이 있으면 styles 태그에 정의된 순서를 따른다 라고 배운 바가 있다. styles 태그는 tailwind 가 작성하므로, 그 순서를 세밀하게 조절할 수 없으며 조절해서도 안된다.props.className에 아무것도 들어오지 않았을 경우undefined클래스가 삽입된다. 이거 잘못하면html에undefined라는 클래스명이 남발될 수도 있다.
이럴 때 우리는 twMerge 를 쓸 수 있다. twMerge 는 tailwind-merge 의 함수로, 서로 중복되는 클래스들을 뒤에 온 것으로 대치해준다. 요컨데, 아래처럼 작성하면 최종적으로는 flex flex-row 만 남는다:
return (<div className={twMerge("flex flex-col", "flex-row")}/>)
똑똑하게도, Falsey 한 값들은 모두 무시해준다. 예를 들어 아래와 같은 것들이 모두 기대한 대로 동작한다:
return (<div className={twMerge("flex flex-col", props.className)}/>) return (<div className={twMerge("flex flex-col", someBooleanCondition && "flex-row")}/>) return (<div className={twMerge("flex flex-col", "")}/>)
솔직한 감상
솔직히 이제 새 프로젝트 파면 tailwind 를 쓸 것 같다.
기존 프로젝트에서 styled 쓰는게 벌써 괴롭다. 새 styled 컴포넌트에 이름 붙히는것도 지치고... css 너무 장황해.... (이런 발언)
후기
매번 블로그를 엎을 때마다 새로 배우는게 있다. 신기하게도... 웹의 변화가 그만큼 급진적이라는 얘기이기도 하겠지만...
그리고 기존 블로그는 내린지 좀 되긴 했지만 아마 계속 올려두었으면 Next 의 10점짜리 취약점에 걸렸을 것이다.
생긴 것도 꽤 이쁘게 잘 나온 것 같고(주변인이 이쁘다고 해줬다), 나름 괜찮은 기능도 하나 추가해서 마음에 들게 잘 나왔다고 생각한다.
다만 댓글 기능을 아직 적용 못했는데, 기존에 버셀에 올려뒀던 낡은 저장소를 로컬로 옮기려고 보니 애가 쿠키를 자꾸 이상하게 구워서 로그인이 안되길래 일단 치웠다.
이 건은 시간과 정신적 여유가 충분할 때 해보는 것으로 하려고 한다. 어차피 아무도 댓글 안달잖아 깃헙 계정이 필요한데 누가 달겠냐








