솔직히 말해서.
이제 부트스트랩이 좀 질렸습니다.
(부트스트랩에게 미안해하면서 하는 말입니다)
초보 개발자로 일하며 다이나믹한 화면단을 구성하는 일을 하지 않고 있기에 부트스트랩만으로도 모든 것이 다 해결 가능하지만 새로운 토이프로젝트에 참여하면서 부트스트랩이 아닌 다른 CSS 프레임워크를 사용해보자는 의견이 나왔고, 뷰단 작업을 도맡게 된 저는 여러 것들을 찾아보다가 말로만 듣던 TailWindCss를 적용해보고자 했어서 기록을 남겨두기 위해 이렇게 포스트도 작성하게 되었습니다.
TailWindCss를 좀 써보고 나서 이 글을 써보면 부트스트랩과의 차이에 대해 느낀 것들을 적을 수 있었을 테지만 설정을 해가는 동시에 글을 작성하고 있는 지라 아마 그 느낌과 생각들은 이후 게시글들에서 더 명확히 느낄 수 있찌 않을까 싶습니다.
그래서 이 글이 1번 글이지만, 아마도, 0번 글이 나중에 정리되는 느낌으로다가 올라오지 않을까 싶네요!
그럼에도 구(글)선생님을 통해 알아본 tailwindcss의 장단점을 가볍게 정리해보자면 아래와 같습니다.
1) 장점 : 기존 CSS의 단점들을 모조리 다 덮어버릴 수 있다.
기존 CSS의 단점으로 크게 대두되는 것은 두 가지가 있습니다.
① 명시성
② 전역 스코프
우선 명시성이라 함은 선택자를 사용함에 있어서 더 확실한 선택자의 구분을 위해 특별한 이름을 사용하는 등의 명시성이 도드라진다는 것을 뜻합니다. 이 작업이 처음에는 괜찮아보일지라도 나중이 되어지면 우선순위를 비틀기 위해 !important를 남발한다던가, 깊이가 너무 깊어진 명시성을 띄게 되어 접근하는 것에 있어서 어려움이 생긴다는 단점이 있습니다(이는 남이 만든 CSS 파일을 수정할 때 아주 머리가 터져버릴 것과 같은 느낌을 받게 되는 결과를 낳게 되지요).
또한 CSS를 사용하기 위해선 전역 스코프를 사용하기 위해 CSS의 클래스 네이밍에 시간을 많이 쏟을 수 밖에 없었습니다. 전역 스코프에 의해 클래스가 중복이 되면 안되고, 최대한 의미있게 작성해서 협업과 유지보수에 용이하도록 하기 위해 고민의 고민을 더했지요. 이는 계속해서 늘어나는 클래스와 끊임없이 내려가는 스크롤은 막을 방법이 없어졌습니다.
하지만 tailwindcss는 utility-first를 개념으로 만들어진 CSS framework입니다. 클래스에 유틸리티한 이름(flex, pt-4, ...)을 붙여 HTML 내에서 개발하는 방식이지요. 관리 측면에서 분리한 둘을 다시 붙여놓는다는 게 아이러니하지만 요즘 개발 방식에 잘 맞고 편한 것 같습니다.
이는 CSS 네이밍에 불필요한 시간을 들이는 것을 방지하고 각각의 CSS 파일을 생성하여 스타일을 정의하던 것들을 방지하는 것이 가능해집니다.
2) 단점
이렇게 장점이 있다면 당연히 단점도 있겠쬬?
아래의 코드를 보실까요?
위의 코드를 보면 가독성을 포기하다 싶기까지 한 코드들처럼 보입니다.
사실 사용자 입장에서는 누가 개발자 모드로 들어와서 각각의 div에 달린 클래스 명들을 하나씩 살피겠나 싶기는 하지만요..
또한 클래스의 이름을 적응하는 데까지 어느정도의 시간이 필요하다는 단점도 있습니다.
길었던 서론은 여기까지하고!!
이제 TailWindCss를 사용해봅시다!!
1. 초기 설정
우선 저희 토이프로젝트는 스프링 부트에 Gradle, Tomcat, Oracle을 사용하는 환경입니다.
TailWindCss도 부트스트랩과 동일하게 헤드단에 필요한 Script를 CDM으로 추가해서 사용할 수 있습니다.
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.tailwindcss.com"></script>
</head>
하지만 위와 같은 방식은 링크 주소가 변경되거나 최신 버전의 링크주소는 위의 주소와 다를 경우 일일이 주소를 수정해줘야한다는 번거로움, 큰 단점이 존재합니다. 또한 인터넷이 되지 않는 환경에서는 저 주소에 접근할 수 없기 때문에 어려움이 있을 수 밖에 없죠.
그렇기 때문에 이번에는 Node.js를 통해서 사전에 라이브러리를 다운받아둔 뒤 배포할 때 다운되어있는 라이브러리를 활용하는 방식으로 접근해보고자 합니다.
** Node.js가 없다면 할 수 없기에 Node.js는 필수입니다
2. NPM 적용해보기
1) npm으로 tailwindcss 인스톨
저는 인텔리제이를 사용하기 때문에 인텔리제이 내부의 터미널을 통해 저의 프로젝트 폴더의 src/resources/static 폴더로 이동하여서 아래와 같은 명령어를 실행시켰습니다.
npm init
npm install (ex: tailwindcss, postcss)
// tailwind.config.js 생성
npm tailwindcss init
static 폴더는 정적 리소스를 제공하는 곳입니다.
위의 작업의 결과로 생성된 package.json에 등록된 라이브러리들을 정적 리소스로 다운받은 후 프로젝트를 빌드할 때 적용시켜주게 될 것입니다.
또한 tailwindcss의 초기화 파일도 함께 생성해주었습니다.
제 package.json 파일은 이렇게 생겼네요.
{
"name": "static",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"autoprefixer": "^10.4.14",
"postcss": "^8.4.27",
"tailwindcss": "^3.3.3"
}
}
위의 작업을 다 마친 후 buile.gradle을 새로고침 한다면 아래와 같이 node.modules 폴더가 생성되고 라이브러리들이 추가된 것을 확인할 수 있답니다!
2) 설정 파일 세팅
이후에는 설정 파일을 만져줘야합니다.
우선 postcss 컨피그 파일을 생성해주고 아래의 내용을 넣어주었습니다.
IE에선 호환이 어려운 부분들이 있기 때문에 postcss로 호환성을 맞춰줍니다.
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
]
}
다음은 tailwind.config.js 파일입니다.
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'../templates/index.html',
'./src/**/*.{html,js}',
],
theme: {
extend: {},
},
plugins: [],
}
여기에서 중요한 포인트!
다른 설정들도 여기서 할 수 있지만 초기 tailwindcss의 설정을 하기 위해서는 content 의 경로를 잘 설정해줘야합니다.
TailWind CSS는 실제로 프로젝트 내에 사용된 클래스들만을 임포트해줍니다.
이는 가벼운 프로젝트 구현을 가능케 하죠.
그렇기 위해서 content의 영역을 지정해줍니다.
여기에 명시되어있는 위치 내의 파일들을 찾아 클래스명을 훑어서 css파일을 만들어주기 때문입니다.
3) CSS 파일 생성
이제 TailWind CSS를 사용하기 위해 CSS파일을 생성해봅시다.
우선 저는 index.html에 테스트 용으로 아래와 같은 코드를 작성했습니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>테스트</title>
<script type="text/javascript" src="https://openapi.map.naver.com/openapi/v3/maps.js?ncpClientId=x6albodpw0"></script>
<link rel="stylesheet" href="/css/tailwind.css">
</head>
<body>
<h1 class="text-4xl font-bold text-center text-blue-500">==Hello World==</h1>
</body>
</html>
여기서 <h1> 태그에 달려있는 클래스들이 바로 TailWind CSS의 클래스들입니다.
그리고 이 클래스들은 tailwind.css가 정의하고 있죠.
물론 이름은 제가 마음대로 정한 것입니다.
이를 생성하기 위해서는 위에서 만들었었던 package.json 파일로 돌아가야 합니다.
다음 게시글은 본격적으로 TailWindCss의 클래스를 활용해보려 합니다!
저는 파일에 다음과 같은 스크립트 구문을 추가했습니다.
{
"name": "static",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"buildcss": "tailwind -i ./css/main.css -o ./css/tailwind.css",
"watch" : "tailwind -i ./css/main.css -o ./css/tailwind.css --watch"
},
"author": "",
"license": "ISC",
"devDependencies": {
"autoprefixer": "^10.4.14",
"postcss": "^8.4.27",
"tailwindcss": "^3.3.3"
}
}
1) buildcss : tailwind가 참조할 글로벌 css 파일을 통해 css 파일을 하나 생성해줍니다.
-i 참조할 파일 이름과 위치 -o 복사될 파일 이름과 위치
2) watch : --watch를 붙여서 자동 빌드가 되도록 합니다. 저의 토이프로젝트의 규모가 는다면 사용하겠지만 테스트를 해보는 지금 입장에서는 별로 필요가 없어서 만들어만 두었습니다.
위의 명령어를 아래와 같이 입력하면 실행이 됩니다.
npm run buildcss
npm run watch
이렇게 tailwind.config.js 파일에 content 부분에 입력한 경로의 파일을 인식하여 자동으로 css 파일을 만들어줍니다.
생성된 css 파일의 코드 전문은 가장 아래에 붙여드립니다.
어렵게만 느껴졌던 TailWind CSS.
차근차근 해보다보니 할만 하네요!!!
다음 게시글로는 TailWind CSS를 실제로 사용해보면서 유용할 것 같은 클래스 명들을 알아보려 합니다.
그럼 다음에 뵈어요~💤
- 참조한 포스트들
https://onlydev.tistory.com/127
https://wonny.space/writing/dev/hello-tailwind-css
< tailwind.css >
/*
! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com
*/
/*
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
*/
*,
::before,
::after {
box-sizing: border-box;
/* 1 */
border-width: 0;
/* 2 */
border-style: solid;
/* 2 */
border-color: #e5e7eb;
/* 2 */
}
::before,
::after {
--tw-content: '';
}
/*
1. Use a consistent sensible line-height in all browsers.
2. Prevent adjustments of font size after orientation changes in iOS.
3. Use a more readable tab size.
4. Use the user's configured `sans` font-family by default.
5. Use the user's configured `sans` font-feature-settings by default.
6. Use the user's configured `sans` font-variation-settings by default.
*/
html {
line-height: 1.5;
/* 1 */
-webkit-text-size-adjust: 100%;
/* 2 */
-moz-tab-size: 4;
/* 3 */
-o-tab-size: 4;
tab-size: 4;
/* 3 */
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
/* 4 */
font-feature-settings: normal;
/* 5 */
font-variation-settings: normal;
/* 6 */
}
/*
1. Remove the margin in all browsers.
2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
*/
body {
margin: 0;
/* 1 */
line-height: inherit;
/* 2 */
}
/*
1. Add the correct height in Firefox.
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
3. Ensure horizontal rules are visible by default.
*/
hr {
height: 0;
/* 1 */
color: inherit;
/* 2 */
border-top-width: 1px;
/* 3 */
}
/*
Add the correct text decoration in Chrome, Edge, and Safari.
*/
abbr:where([title]) {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
}
/*
Remove the default font size and weight for headings.
*/
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: inherit;
}
/*
Reset links to optimize for opt-in styling instead of opt-out.
*/
a {
color: inherit;
text-decoration: inherit;
}
/*
Add the correct font weight in Edge and Safari.
*/
b,
strong {
font-weight: bolder;
}
/*
1. Use the user's configured `mono` font family by default.
2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp,
pre {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
/* 1 */
font-size: 1em;
/* 2 */
}
/*
Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/*
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/*
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
3. Remove gaps between table borders by default.
*/
table {
text-indent: 0;
/* 1 */
border-color: inherit;
/* 2 */
border-collapse: collapse;
/* 3 */
}
/*
1. Change the font styles in all browsers.
2. Remove the margin in Firefox and Safari.
3. Remove default padding in all browsers.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit;
/* 1 */
font-feature-settings: inherit;
/* 1 */
font-variation-settings: inherit;
/* 1 */
font-size: 100%;
/* 1 */
font-weight: inherit;
/* 1 */
line-height: inherit;
/* 1 */
color: inherit;
/* 1 */
margin: 0;
/* 2 */
padding: 0;
/* 3 */
}
/*
Remove the inheritance of text transform in Edge and Firefox.
*/
button,
select {
text-transform: none;
}
/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Remove default button styles.
*/
button,
[type='button'],
[type='reset'],
[type='submit'] {
-webkit-appearance: button;
/* 1 */
background-color: transparent;
/* 2 */
background-image: none;
/* 2 */
}
/*
Use the modern Firefox focus style for all focusable elements.
*/
:-moz-focusring {
outline: auto;
}
/*
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
*/
:-moz-ui-invalid {
box-shadow: none;
}
/*
Add the correct vertical alignment in Chrome and Firefox.
*/
progress {
vertical-align: baseline;
}
/*
Correct the cursor style of increment and decrement buttons in Safari.
*/
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
height: auto;
}
/*
1. Correct the odd appearance in Chrome and Safari.
2. Correct the outline style in Safari.
*/
[type='search'] {
-webkit-appearance: textfield;
/* 1 */
outline-offset: -2px;
/* 2 */
}
/*
Remove the inner padding in Chrome and Safari on macOS.
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button;
/* 1 */
font: inherit;
/* 2 */
}
/*
Add the correct display in Chrome and Safari.
*/
summary {
display: list-item;
}
/*
Removes the default spacing and border for appropriate elements.
*/
blockquote,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
figure,
p,
pre {
margin: 0;
}
fieldset {
margin: 0;
padding: 0;
}
legend {
padding: 0;
}
ol,
ul,
menu {
list-style: none;
margin: 0;
padding: 0;
}
/*
Reset default styling for dialogs.
*/
dialog {
padding: 0;
}
/*
Prevent resizing textareas horizontally by default.
*/
textarea {
resize: vertical;
}
/*
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
2. Set the default placeholder color to the user's configured gray 400 color.
*/
input::-moz-placeholder, textarea::-moz-placeholder {
opacity: 1;
/* 1 */
color: #9ca3af;
/* 2 */
}
input::placeholder,
textarea::placeholder {
opacity: 1;
/* 1 */
color: #9ca3af;
/* 2 */
}
/*
Set the default cursor for buttons.
*/
button,
[role="button"] {
cursor: pointer;
}
/*
Make sure disabled buttons don't get the pointer cursor.
*/
:disabled {
cursor: default;
}
/*
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
This can trigger a poorly considered lint error in some tools but is included by design.
*/
img,
svg,
video,
canvas,
audio,
iframe,
embed,
object {
display: block;
/* 1 */
vertical-align: middle;
/* 2 */
}
/*
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
*/
img,
video {
max-width: 100%;
height: auto;
}
/* Make elements with the HTML hidden attribute stay hidden by default */
[hidden] {
display: none;
}
*, ::before, ::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
}
::backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
}
.text-center {
text-align: center;
}
.text-4xl {
font-size: 2.25rem;
line-height: 2.5rem;
}
.font-bold {
font-weight: 700;
}
.text-blue-500 {
--tw-text-opacity: 1;
color: rgb(59 130 246 / var(--tw-text-opacity));
}