๐Ÿ’ป 2024๋…„ ํ•˜๋ฐ˜๊ธฐ ๊ณต์œ  2

1. MOCKING TOOL๋กœ ์ƒ์‚ฐ์„ฑ ์ฑ™๊ธฐ๊ธฐ

๋ชจํ‚น(Mocking)์ด๋ž€ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ž‘์„ฑํ•  ๋•Œ, ํ•ด๋‹น ์ฝ”๋“œ๊ฐ€ ์˜์กดํ•˜๋Š” ๋ถ€๋ถ„์„ ๊ฐ€์งœ(mock)๋กœ ๋Œ€์ฒดํ•˜๋Š” ๊ธฐ๋ฒ•์ž…๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ• ๋•Œ ๋ฐฑ์—”๋“œ์—์„œ API๊ฐ€ ์™„์„ฑ๋˜์ง€ ์•Š์•„ ๋‚œ์ฒ˜ํ–ˆ๋˜ ์ ์ด ์žˆ์—ˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํŠน์ • ์ƒํ™ฉ์˜ ๊ฐœ๋ฐœ๋˜์ง€ ์•Š์€ ๋ถ€๋ถ„์— ์˜์กดํ•˜์—ฌ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ์•ผ๊ธฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ๊ฐœ๋ฐœ๋˜์ง€ ์•Š์€ ๋ชจ๋“ˆ์— ์˜์กดํ•˜๋ฉด ํ…Œ์ŠคํŒ…์ด ์–ด๋ ค์›€
  2. ๋‹ค๋ฅธ ๋ชจ๋“ˆ์— ์˜ํ•ด ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๊ฐ€ ๋ฐ”๋€”์ˆ˜์žˆ๊ณ , ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์†๋„๊ฐ€ ๋–จ์–ด์ง
  3. ํ”„๋กœ์ ํŠธ ๊ทœ๋ชจ๊ฐ€ ์ปค์ ธ์„œ ํ…Œ์ŠคํŠธ ์ผ€์ดํฌ๊ฐ€ ๋งŽ์•„์งˆ ๊ฒฝ์šฐ ํฐ ์ด์Šˆ๊ฐ€ ์•ผ๊ธฐ๋  ์ˆ˜ ์žˆ์Œ

์ด๋Ÿฌํ•œ ์ƒํ™ฉ์—์„œ ์‹ค์ œ ๋ชจ๋“ˆ์„ ๋ชจ๋ฐฉํ•œ ๊ฐ€์งœ ๋ชจ๋“ˆ์„ ์ƒ์„ฑํ•˜๋Š” mocking ์ด ํ•„์š”ํ•˜๊ฒŒ ๋˜๋ฉฐ, ์‹ค์ œ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ๋„ mocking ํ•˜๋Š” ํ–‰์œ„๊ฐ€ ๋น ๋ฅธ ๊ฐœ๋ฐœ๊ณผ ์•ˆ์ •์„ฑ์— ๋„์›€์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ๋ฐฑ์—”๋“œ ๋ชจํ‚น์ด ํ•„์š”ํ•œ ์ƒํ™ฉ์€ ๋‹ค์–‘ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฐฑ์—”๋“œ API๊ฐ€ ์•„์ง ๊ฐœ๋ฐœ ์ค‘์ด๊ฑฐ๋‚˜, ์›ํ•˜๋Š” ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋‚˜ ์‘๋‹ต์„ ํ…Œ์ŠคํŠธํ•ด์•ผ ํ•  ๋•Œ, ๋˜๋Š” ์„œ๋ฒ„ ์—๋Ÿฌ ์ƒํ™ฉ์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์‹ถ์„ ๋•Œ ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ์ƒํ™ฉ์—์„œ์˜ ์†”๋ฃจ์…˜์€ ๋Œ€ํ‘œ์ ์œผ๋กœ ์„ธ๊ฐ€์ง€๋กœ ๋‚˜๋ˆ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ,

  1. ๋ฐฑ์—”๋“œ ์ž‘์—…์ด ์™„๋ฃŒ๋ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ๋‹ค.
  2. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด๋ถ€ ๋กœ์ง์— ์ง์ ‘ Mocking ํ•œ๋‹ค. (์ฐธ๊ณ  : publicย folder์™€ย .jsonย ํŒŒ์ผ ๋“ฑ์„ ์ด์šฉํ•˜์—ฌ mocking )
  3. ํŽ˜์ดํฌ API๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

ํ˜„์‹ค์ ์œผ๋กœ ์‹œ๊ฐ„์ด ๋งŽ๋‹ค๋ฉด 1๋ฒˆ์„ ์ง€ํ–ฅํ•˜๊ฒ ์ง€๋งŒ, ์‹œ๊ฐ„์ด ๋ถ€์กฑํ•˜๊ฑฐ๋‚˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋ถ€์กฑํ•  ๊ฒฝ์šฐ์—๋Š” 3๋ฒˆ์„ ์„ ํƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ ์ฉ์ฉ๋ฐ•์‚ฌ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Google API ํ• ๋‹น๋Ÿ‰ ๋ถ€์กฑ์œผ๋กœ ์ธํ•ด ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด 3๋ฒˆ ๋ฐฉ๋ฒ•์„ ์ฑ„ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ด์™€ ๊ด€๋ จํ•˜์—ฌ ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ํˆด๋“ค์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

Json Placeholder

JSONPlaceholder - Free Fake REST API

Json Placeholder๋Š” ํ”„๋กœํ† ํƒ€์ž… ๋˜๋Š” ๊ฐœ๋ฐœ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ํŽ˜์ดํฌ JSON API๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

6๊ฐœ ์œ ํ˜•์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ, CRUD ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ๋ชจ๋“  HTTP ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

GET, POST, PUT, PATCH, DELETE ๋ฉ”์†Œ๋“œ๋ฅผ ๋ชจ๋‘ ํ…Œ์ŠคํŠธ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ฒŒ์‹œ๋ฌผ, ์‚ฌ์ง„ ๋“ฑ๊ณผ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋“ค๋„ ๋‹ค๋ฃฐ์ˆ˜ ์žˆ๊ฒŒ ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ„๋žตํ•œ ์‚ฌ์šฉ ์˜ˆ์‹œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

// ์š”์ฒญ ์˜ˆ์‹œ
fetch("https://jsonplaceholder.typicode.com/todos/1")
  .then((response) => response.json())
  .then((json) => console.log(json));

Json Placeholder๋ฅผ ์ด์šฉํ•˜๋ฉด ๋น ๋ฅธ ์†๋„๋กœ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์‘๋‹ต๊ฐ’์„ ์ปค์Šคํ…€ํ•˜๋Š” ๊ฒƒ์ด ์–ด๋ ค์šด ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

MSW(Mock Service Worker)

Mock Service Worker

MSW(Mock Service Worker)๋Š” Service Worker๋ฅผ ์ด์šฉํ•ด ์„œ๋ฒ„๋ฅผ ํ–ฅํ•œ ์‹ค์ œ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„์„œ(intercept) ๋ชจ์˜ ์‘๋‹ต(Mocked response)๋ฅผ ๋ณด๋‚ด์ฃผ๋Š” API Mocking ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. ์‹ค์ œ API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ ๋„คํŠธ์›Œํฌ ์ˆ˜์ค€์—์„œ Mockingํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ ์˜ˆ์‹œ๋กœ POST ๋ฉ”์„œ๋“œ์˜ /login ์š”์ฒญ์„ ๋งŒ๋“ค์–ด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด API๋ฅผ ์ง์ ‘ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

/ src/mocks.js
import { setupWorker, rest } from 'msw'

const worker = setupWorker(
  rest.post('/login', (req, res, ctx) => {
    const isAuthenticated = sessionStorage.getItem('username')

    if (!isAuthenticated) {
      return res(
        ctx.status(403),
        ctx.json({
          errorMessage: 'Not authenticated',
        }),
      )
    }
    return res(
      ctx.json({firstName: 'John'}),
    )
  }),
)

// Register the Service Worker and enable the mocking
worker.start()

์ด๋Ÿฐ์‹์œผ๋กœ ์ƒํƒœ ์ฝ”๋“œ์™€ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€, ๋ฐ˜ํ™˜ ๊ฐ’๋“ค์„ ์ปค์Šคํ…€ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์—ฌ๋Ÿฌ ์˜ต์…˜๋“ค์„ ์„ธ๋ถ€์ ์œผ๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. (์‹ค์ œ API ์ฒ˜๋Ÿผ ์ •๋ง ๋‹ค์–‘ํ•˜๊ฒŒ ์ปค์Šคํ…€ ํ•  ์ˆ˜ ์žˆ๋‹ค)

ํ•˜์ง€๋งŒ ์„ค์ • ๋น„์šฉ์ด ๋น„๊ต์  ๋งŽ์ด ๋“ค๊ณ , ๊ฐ„๋‹จํ•œ ์‘๋‹ต ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด์„œ๋Š” ๋‹ค์†Œ ๋ฒˆ๊ฑฐ๋กœ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

conclusion

  • FE ํ™˜๊ฒฝ์˜ ๋น ๋ฅธ ๊ฐœ๋ฐœ์„ ์œ„ํ•œ๋‹ค๋ฉด ๋ฐฑ์—”๋“œ ๋ชจํ‚น์ด ํ•„์š”ํ•˜๋‹ค.
  • Json Placeholder๋Š” ๊ฐ„๋‹จํ•œ ์‘๋‹ต ํ…Œ์ŠคํŠธ์— ์ ํ•ฉํ•˜๋ฉฐ, MSW๋Š” ๋”์šฑ ๋ณต์žกํ•œ ์ƒํ™ฉ์—์„œ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

reference

https://tech.kakao.com/2021/09/29/mocking-fe/

https://developer.mozilla.org/ko/docs/Web/API/Service_Worker_API

https://www.daleseo.com/mock-service-worker/#google_vignette

--

2. ๋กœ๋”ฉ ์‹œ๊ฐ„์— ๋”ฐ๋ฅธ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ์ฒ˜๋ฆฌ

ํ”„๋กœ๊ทธ๋ž˜์Šค ๋ฐ”๋‚˜ ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ์™€ ๊ฐ™์€ UI ์š”์†Œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ํ˜„์žฌ ์ž‘์—… ์ƒํƒœ๋ฅผ ํŒŒ์•…ํ•˜๊ณ  ๋ถˆํ™•์‹ค์„ฑ์„ ์ค„์—ฌ์ฃผ์–ด ํ”„๋กœ์„ธ์Šค๋ฅผ ๋” ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค. ์ด๋กœ์จ ์‚ฌ์šฉ์ž๋Š” ๋”์šฑ ๋งŒ์กฑ์Šค๋Ÿฌ์šด ๊ฒฝํ—˜์„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ €ํฌ ์„œ๋น„์Šค์—์„œ๋„ ๋ฐ์ดํ„ฐ Fetching ์‹œ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์„ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด Suspense๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•์ด ์‹ค์ œ๋กœ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ค๊ณ  ์žˆ๋Š”์ง€ ์˜๋ฌธ์ž…๋‹ˆ๋‹ค.

##Progress Indicator ์— ๊ด€ํ•œ ๋ฆฌ์„œ์น˜

์„ธ๊ณ„์ ์ธ UX ์—ฐ๊ตฌ์ž ๋‹์Šจ ๋†€๋จผ์˜ ์ง€์นจ์„ ์‚ดํŽด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • 1์ดˆ ์ด์ƒ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์—๋Š” Progress Indicator๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ž‘์—…์ด 1์ดˆ ๋ฏธ๋งŒ์— ์™„๋ฃŒ๋˜๋Š” ๊ฒฝ์šฐ ๋ฐ˜๋ณต๋˜๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜์€ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
  • ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ์™€ ๊ฐ™์€ ์š”์†Œ๋Š” 2~9์ดˆ์˜ ์ง€์—ฐ ์‹œ์—๋งŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์™„๋ฃŒ์œจ ์• ๋‹ˆ๋ฉ”์ด์…˜์€ 10์ดˆ ์ด์ƒ ์†Œ์š”๋˜๋Š” ์ž‘์—…์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๋น ๋ฅธ ์‘๋‹ต์ด ๊ฐ€์žฅ ์ด์ƒ์ ์ด์ง€๋งŒ, ์„œ๋ฒ„ ์—…๊ทธ๋ ˆ์ด๋“œ๋กœ ์ธํ•ด ์‹œ์Šคํ…œ ์†๋„๊ฐ€ ๋А๋ ค์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ž‘์—…์ด 10์ดˆ ์ด์ƒ ์†Œ์š”๋  ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž์—๊ฒŒ ์™„๋ฃŒ ์˜ˆ์ƒ ์‹œ๊ฐ„์„ ํ‘œ์‹œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์ •์ ์ธ ์ง„ํ–‰๋ฅ  ํ‘œ์‹œ๋Š” ํ”ผํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. (์˜ˆ: ํ™”๋ฉด์— ๋‹จ์ˆœํžˆ "...๋กœ๋“œ ์ค‘" ํ…์ŠคํŠธ๋งŒ ํ‘œ์‹œ)

์ด๋Ÿฌํ•œ ์ง€์นจ์„ ๊ณ ๋ คํ•˜๋ฉด ๋กœ๋”ฉ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋กœ๋”ฉ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ, ๋ชจ๋“  ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹Œ 1์ดˆ ์ด์ƒ ์ง€์—ฐ๋  ๊ฒฝ์šฐ์—๋งŒ ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

##React Suspense ๋ฅผ ์ด์šฉํ•˜์—ฌ ๋กœ๋”ฉ ๊ฒฝํ—˜ ๊ฐœ์„ 

ํ˜„์žฌ ์ €ํฌ๋Š” ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ Suspense๋ฅผ ์ด์šฉํ•˜์—ฌ child component๋ฅผ ๊ฐ์‹ธ ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

// example
<div className={styles.page__depart}>
  <React.Suspense fallback={<LoadingSpinner />}>
    <DeptListbox />
  </React.Suspense>
</div>

์ด๋ฅผ ๋กœ๋”ฉ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ๋‹ค๋ฅธ UX๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋„๋ก ์œ ํ‹ธ์„ฑ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ™œ์šฉํ•ด ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, 1์ดˆ๊นŒ์ง€๋Š” ์•„๋ฌด๋Ÿฐ UX๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š๋‹ค๊ฐ€ 1์ดˆ ์ด์ƒ ๋กœ๋”ฉ์ด ์ง€์—ฐ๋œ๋‹ค๋ฉด ๊ทธ๋•Œ ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” ์นด์นด์˜ค์—์„œ ์ œ์‹œํ•œ ์œ ํ‹ธ ์ปดํฌ๋„ŒํŠธ ์„ค๊ณ„์ž…๋‹ˆ๋‹ค.

const DeferredComponent = ({ children }: PropsWithChildren<{}>) => {
  const [isDeferred, setIsDeferred] = useState(false);

  useEffect(() => {
    // 200ms ์ง€๋‚œ ํ›„ children Render
    const timeoutId = setTimeout(() => {
      setIsDeferred(true);
    }, 200);
    return () => clearTimeout(timeoutId);
  }, []);

  if (!isDeferred) {
    return null;
  }

  return <>{children}</>;
};

**DeferredComponent**๋Š” children์„ Props๋กœ ๋ฐ›๊ณ , 200ms ์ด์ „์—๋Š” children์„ ๋ Œ๋”๋งํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๊ธฐ์กด์˜ ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ์— ์ด **DeferredComponent**๋ฅผ ์ ์šฉํ•˜๋ฉด ํŠน์ • ์‹œ์ ๋ถ€ํ„ฐ ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ๊ฐ€ ๋ณด์—ฌ์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

<div className={styles.page__depart}>
  <React.Suspense
    fallback={
      <DeferredComponent>
        <LoadingSpinner />
      </DeferredComponent>
    }
  >
    <DeptListbox />
  </React.Suspense>
</div>

์šฐ๋ฆฌ๋Š” ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•ด ํ•ญ์ƒ Progress Indicator(=Skeleton)์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋น ๋ฅธ ์ธํ„ฐ๋„ท ํ™˜๊ฒฝ์—์„œ๋Š” ์˜คํžˆ๋ ค ์„ ํƒ์ ์œผ๋กœ Progress Indicator๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด ๋” ๋‚˜์€ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

resource

  • https://tech.kakaopay.com/post/skeleton-ui-idea/
  • https://www.nngroup.com/articles/progress-indicators/

--

3. Web vital ์ตœ์ ํ™”๋ฅผ ์œ„ํ•œ tip

์›น ์„ฑ๋Šฅ ์ตœ์ ํ™”๋Š” ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ์ฃผ์š” ์š”์†Œ๋กœ, ์„œ๋น„์Šค ๊ด€์ ์—์„œ๋„ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ๋กœ๋”ฉ ์†๋„๊ฐ€ ๋А๋ฆด์ˆ˜๋ก ์‚ฌ์šฉ์ž๋Š” ์›น์‚ฌ์ดํŠธ๋ฅผ ๋– ๋‚  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์•„์ง€๋Š”๋ฐ, 0.1์ดˆ์˜ ์„ฑ๋Šฅ ๊ฐœ์„ ๋„ conversion rate๋ฅผ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฃผ๋กœ LCP, FID, INP์™€ ๊ฐ™์€ web vitals๋กœ ์ธก์ •๋˜๋ฉฐ, ์ด๋Ÿฌํ•œ ์ง€ํ‘œ๋Š” ์‚ฌ์šฉ์ž์˜ ์ฒซ ๋ฐ˜์‘์„ ๊ธฐ์ค€์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค. (์ตœ๊ทผ์—๋Š” ์ด๋ฅผ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด INP๊ฐ€ ๋„์ž…๋˜์—ˆ๋Š”๋ฐ, ์ด๋Š” ๋ชจ๋“  ์ž…๋ ฅ ์ง€์—ฐ ์‹œ๊ฐ„์˜ ํ‰๊ท ์„ ์ธก์ •ํ•˜๋Š” ๋“ฑ ๋” ๋‚˜์€ ์ธก์ •์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.)

์ด๋Ÿฌํ•œ web vitals ๋ฅผ ์–ด๋–ป๊ฒŒ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€, ์—ฌ๋Ÿฌ๊ฐ€์ง€ ํŒ๋“ค์„ ์ •๋ฆฌํ•ด๋ดค์Šต๋‹ˆ๋‹ค.

LCP ์ตœ์ ํ™”

  1. LCP ์ž์›์ด HTML ๋‚ด์—์„œ ๋นจ๋ฆฌ ์ฐพ์•„์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  2. LCP ์ž์›์ด ์šฐ์„ ์‹œ๋˜์–ด ๋‹ค์šด๋กœ๋“œ๋  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  3. CDN์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ˆ์„ ํˆฌ์žํ•ฉ๋‹ˆ๋‹ค.
    • ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
    • ๋ฌผ๋ฆฌ์ ์œผ๋กœ ์ตœ๋Œ€ํ•œ ๊ฐ€๊นŒ์šด ์„œ๋ฒ„๋ฅผ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.

์‹ค์ œ ์‚ฌ๋ก€:

  • ์ด๋ฏธ์ง€๋Š” <img> ์—˜๋ฆฌ๋จผํŠธ์— ๋„ฃ๊ณ  src๋‚˜ srcset ์†์„ฑ์„ ์ด์šฉํ•˜์—ฌ ์ฒซ ๋ฒˆ์งธ ๋ Œ๋”๋ง ์‹œ ํ•„์š”ํ•œ ์ด๋ฏธ์ง€๋ฅผ ์•Œ๋ ค์ค๋‹ˆ๋‹ค.
  • SSR์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๋‚ด๋ถ€์—์„œ ํ˜ธ์ŠคํŒ…ํ•˜์ง€ ์•Š๋Š” ์ด๋ฏธ์ง€์—๋Š” <link rel="preload"> ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €์—๊ฒŒ ๋นจ๋ฆฌ ๋กœ๋“œํ•ด์•ผ ํ•˜๋Š” ์†Œ์Šค์ž„์„ ์•Œ๋ ค์ค๋‹ˆ๋‹ค.
  • <img> ํƒœ๊ทธ์— fetchpriority="high" ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜์—ฌ LCP ์ด๋ฏธ์ง€๋ฅผ ์šฐ์„ ์ ์œผ๋กœ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋‹ค์šด๋กœ๋“œํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  • <img> ์—˜๋ฆฌ๋จผํŠธ์— loading="lazy" ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜์—ฌ LCP ๊ฐ€ ์•„๋‹Œ ์ด๋ฏธ์ง€๋ฅผ ๋‚˜์ค‘์— ๋กœ๋”ฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

CLS ์ตœ์ ํ™”

  • ์ปจํ…์ธ ์˜ ๋ช…ํ™•ํ•œ ์‚ฌ์ด์ฆˆ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • ํ”๋“ค๋ฆฌ๋Š” CSS ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ฐ ์ „ํ™˜ ํšจ๊ณผ๋ฅผ ์ง€์–‘ํ•ฉ๋‹ˆ๋‹ค.
  • bfcache๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์บ์‹ฑ์„ ์ตœ์ ํ™”ํ•ฉ๋‹ˆ๋‹ค.
    • ๋ชจ๋“  ์ง€ํ‘œ๋ฅผ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.
    • ์ด๋ฅผ ์œ„ํ•ด ์บ์‹ฑ์„ ๋ง‰์•„๋†“์ง€ ์•Š์•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

์‹ค์ œ ์‚ฌ๋ก€:

  • width์™€ height๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
  • aspect-ratio๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ width๋งŒ ์ง€์ •ํ•˜๊ณ  height๋ฅผ ์œ ๋™์ ์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • min-height๋ฅผ ์ง€์ •ํ•˜์—ฌ ์š”์†Œ๊ฐ€ ๋œ ๋ฐ€๋ฆฌ๋„๋ก ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

FID ์ตœ์ ํ™”

  • ๊ธด ์ž‘์—…์„ ์ž‘๊ฒŒ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ถˆํ•„์š”ํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ฅผ ์ตœ์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค.
  • ํฐ ๋ Œ๋”๋ง ์—…๋ฐ์ดํŠธ๋ฅผ ํ”ผํ•ฉ๋‹ˆ๋‹ค.
    • ๋ถ€๋ถ„์ ์œผ๋กœ๋งŒ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

์‹ค์ œ ์‚ฌ๋ก€:

  • ๊ธด ์ž‘์—…์„ ์ž‘๊ฒŒ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค.
    • webpack๊ณผ ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ chunking์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฉ”์ธ ์“ฐ๋ ˆ๋“œ์—์„œ ์ž ์‹œ ์ž์›์„ ์–‘๋ณดํ•ฉ๋‹ˆ๋‹ค.
  • coverage ๋„๊ตฌ๋ฅผ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์ฝ”๋“œ๋ฅผ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ถˆํ•„์š”ํ•œ ํŠธ๋ž˜ํ‚น ํƒœ๊ทธ๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
  • requestAnimationFrame() ์‚ฌ์šฉ์„ ์ง€์–‘ํ•˜๊ณ  DOM ํฌ๊ธฐ๋ฅผ ์ตœ์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค.

reference

https://web.dev/articles/vitals

23๋…„ 6์›” Tech ์„ธ๋ฏธ๋‚˜ - ์›น ํ”„๋ก ํŠธ์—”๋“œ ์„ฑ๋Šฅ ์ตœ์ ํ™” ๋ฐฉ๋ฒ• ๋ฐ ์ ์šฉ ์‚ฌ๋ก€

--

4. ์žฌ๋ฏธ์™€ ์ด์ต์„ ์œ„ํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ „๋ฌธํ™”

https://velog.io/@surim014/optimizing-javascript-for-fun-and-for-profit#9-์ „๋ฌธํ™”specialization-์‚ฌ์šฉํ•˜๊ธฐ ์ด ๊ธ€์€ JavaScript ์ตœ์ ํ™” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

์ด์ค‘์—์„œ 9๋ฒˆ ์ฑ•ํ„ฐ์ธ ์ „๋ฌธํ™”(specialization) ์— ๊ด€๋ จํ•ด ํฅ๋ฏธ๋กญ๊ฒŒ ๋А๋‚€ ๋ถ€๋ถ„์„ ์ค‘์‹ฌ์œผ๋กœ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด์™ธ์˜ ๋‚ด์šฉ์ด ๊ถ๊ธˆํ•˜์‹  ๋ถ„๋“ค์€ ์œ„ ๊ธ€์˜ ๋‚˜๋จธ์ง€ ์ฑ•ํ„ฐ๋“ค์„ ์ฝ์–ด๋ณด์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์ „๋ฌธํ™”

์ „๋ฌธํ™”๋Š” ํŠน์ • ์‚ฌ์šฉ ์‚ฌ๋ก€์˜ ์ œ์•ฝ ์กฐ๊ฑด์„ ๋กœ์ง์— ๋งž์ถ”๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ ํ•œ ์กฐ๊ฑด์ด ๋‹ค๋ฅธ ์กฐ๊ฑด๋ณด๋‹ค ๋” ์ž์ฃผ ๋ฐœ์ƒํ•œ๋‹ค๊ณ  ํŒ๋‹จ๋  ๋•Œ, ์ด๋ฅผ ํŒŒ์•…ํ•˜๊ณ  ํ•ด๋‹น ์กฐ๊ฑด์— ๋งž๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์–ด๋–ค ์ œํ’ˆ ๋ชฉ๋ก์— ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์—์„œ, ํƒœ๊ทธ๊ฐ€ ๋Œ€๋ถ€๋ถ„ ๋น„์–ด์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ด ์ƒํ™ฉ์— ๋งž์ถ”์–ด ์ „๋ฌธํ™”๋ฅผ ์ ์šฉํ•˜๊ณ  ์ด์— ๋Œ€ํ•ด ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์˜ˆ์‹œ ๋ฌผํ’ˆ๊ณผ ํƒœ๊ทธ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

const descriptions = ["apples", "oranges", "bananas", "seven"];
const someTags = {
  apples: "::promotion::",
};
const noTags = {};

๊ทธ๋Ÿฐ ๋‹ค์Œ, ์ „๋ฌธํ™”๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š์€ ๋ฌธ์ž์—ด ๋ณ€ํ™˜ ํ•จ์ˆ˜๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

// ํ•ด๋‹น๋˜๋Š” ๊ฒฝ์šฐ ํƒœ๊ทธ์™€ ํ•จ๊ป˜ ์ œํ’ˆ์„ ๋ฌธ์ž์—ด๋กœ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
function productsToString(description, tags) {
  let result = "";
  description.forEach((product) => {
    result += product;
    if (tags[product]) result += tags[product];
    result += ", ";
  });
  return result;
}

์ด์™€ ๋‹ค๋ฅด๊ฒŒ ๋นˆํƒœ๊ทธ๋ฅผ ๋จผ์ € ๊ฑธ๋Ÿฌ์ฃผ๋Š” ์ „๋ฌธํ™”๊ฐ€ ์ ์šฉ๋œ ๋™์ผํ•œ ๊ธฐ๋Šฅ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

function productsToStringSpecialized(description, tags) {
  // ์šฐ๋ฆฌ๋Š” `tags`๊ฐ€ ๋น„์–ด ์žˆ์„ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์œผ๋ฏ€๋กœ ๋ฏธ๋ฆฌ ํ•œ ๋ฒˆ ํ™•์ธํ•œ ๋‹ค์Œ ๋‚ด๋ถ€ ๋ฃจํ”„์—์„œ `if` ๊ฒ€์‚ฌ๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  if (isEmpty(tags)) {
    let result = "";
    description.forEach((product) => {
      result += product + ", ";
    });
    return result;
  } else {
    // ๋ฌธ์ž์—ด ์ „ํ™˜ ๋กœ์ง์€ ๋™์ผ
  }
}
function isEmpty(o) {
  for (let _ in o) {
    return false;
  }
  return true;
}

์ด ๋‘๊ฐœ์˜ ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ง€๊ณ  ์‹ค์ œ ๋น„๊ตํ–ˆ์„ ๋•Œ์˜ ์ˆ˜์น˜ ๋น„๊ต๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค.

  • non speciailized : 85.71 %
  • specialized : 100 %

์œ„์™€ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ ์šฉ์„ ํ†ตํ•ด ์„ฑ๋Šฅ์ด ๋” ๋†’์•„์งˆ ์ˆ˜๋Š” ์žˆ์ง€๋งŒ, ์กฐ๊ฑด์ด ๋‹ฌ๋ผ์ง„๋‹ค๋ฉด ๋‹ค๋ฅด๊ฒŒ ํŒ๋‹จํ•ด์•ผ ํ•  ํ•„์š”๊ฐ€ ์žˆ์„๊ฒƒ์ž…๋‹ˆ๋‹ค.

(์ฝ”๋“œ์—์„œ์˜ ๋ถ„๊ธฐ ์ธก๋ณ€์—์„œ๋„ ์ œ๊ฑฐํ•˜๋Š”๊ฒŒ ๋” ํšจ์œจ์ ์ด๋‹ค ๋ผ๋Š” ์ด๋ฉด์ด ์žˆ๊ธฐ์—.. ๋Œ€ํ‘œ ์˜ˆ์‹œ)

reference : https://velog.io/@surim014/optimizing-javascript-for-fun-and-for-profit#9-์ „๋ฌธํ™”specialization-์‚ฌ์šฉํ•˜๊ธฐ

https://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-processing-an-unsorted-array

--

5. ์‹œ๊ฐ„ ๋‹จ์ถ•์„ ์œ„ํ•œ Tailwind ์‚ฌ์šฉ๊ธฐ

Tailwind๋Š” ์ตœ๊ทผ ์›น ๋ถ„์•ผ์—์„œ ์ธ๊ธฐ๋ฅผ ๋Œ๊ณ ์žˆ๋Š” CSS ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๊ฐœ๋ฐœ์ž๋“ค ์‚ฌ์ด์—์„œ๋„ ํ˜ธ๋ถˆํ˜ธ๊ฐ€ ๋งŽ์ด ๊ฐˆ๋ฆฌ๋Š” ํŽธ์ด๋ฉฐ, ์—ฌ๋Ÿฌ ๋…ผ์Ÿ์ด ์˜ค๊ฐ€๋Š” ํ•ซํฌํ…Œ์ดํ† ๋กœ ์—ฌ๊ฒจ์ง‘๋‹ˆ๋‹ค.

Tailwind๊ฐ€ ์™œ ์ฐจ์„ธ๋Œ€ css ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ๋–ด๋Š”์ง€ ์•Œ์•„๋ณด๊ณ , ์ €์˜ ์ง€๊ทนํžˆ ์ฃผ๊ด€์ ์ธ ๋ฆฌ๋ทฐ๋„ ๊ฐ„๋‹จํžˆ ์ ์–ด๋ณด๋ คํ•ฉ๋‹ˆ๋‹ค.

Tailwind?

Tailwind๋Š” ๋งŽ์€ ์œ ํ‹ธ๋ฆฌํ‹ฐ(utility) ํด๋ž˜์Šค๋กœ ์ด๋ฃจ์–ด์ง„ CSS ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.

์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๋ž€ ํ•œ๊ฐ€์ง€ ์ผ๋งŒ ํ•˜๋Š” ๋งค์šฐ ์ ์€ ์–‘์˜ css ์ฝ”๋“œ๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” ํด๋ž˜์Šค๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋˜๋Š”๋ฐ, ์‹ค์ œ tailwind ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ํฌ๋กฌ inspector๋กœ ์„ ํƒํ•ด๋ณด๋ฉด, ๊ฐ ์Šคํƒ€์ผ์— ํ•œ ํด๋ž˜์Šค๊ฐ€ ํ• ๋‹น๋˜์–ด ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ณดํ†ต์˜ css ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ ค๋ฉด css ํŒŒ์ผ์„ ๋ถ„๋ฆฌํ•˜๊ฑฐ๋‚˜ ์ธ๋ผ์ธ ์š”์†Œ๋กœ ์ง์ ‘ ์ž‘์„ฑํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋Ÿฌ๋‚˜ Tailwind๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด HTML ์š”์†Œ์˜ class ์†์„ฑ์— ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ์Šคํƒ€์ผ๋ง์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. (Tailwind์˜ PostCSS ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค)

๋”ฐ๋ผ์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด Tailwind ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด,

<button class="rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-600">
  Primary
</button>

๋‹ค์Œ๊ณผ ๊ฐ™์€ css ํด๋ž˜์Šค๋“ค์ด ํ•ฉ์ณ์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

.rounded {
  border-radius: 0.25rem;
}

.bg-blue-500 {
  background-color: rgb(59 130 246);
}

.px-4 {
  padding-left: 1rem;
  padding-right: 1rem;
}

...

๋‹ค๋ฅธ CSS ํ”„๋ ˆ์ž„์›Œํฌ์™€์˜ ๋น„๊ต

๋Œ€ํ‘œ์ ์ธ Bootstrap ์ด๋‚˜ Mererial UI ๋“ฑ๊ณผ๋Š” ๋‹ค๋ฅด๊ฒŒ Tailwind๊ฐ€ ๊ฐ€์ง„ ๊ฐ•์ ์€ ๋ฌด์—‡์ผ๊นŒ์š”?

Bootstrap๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ๋Š” <button class="btn btn-primary"> ํ•œ์ค„์ด๋ฉด ์œ„์˜ UI ์™€ ๋™์ผํ•˜๊ฒŒ ๊ตฌํ˜„ํ•ด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, ์„ธ๋ถ€์ ์ธ CSS ์ˆ˜์ •์ด ๋ถˆ๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๋ถˆํŽธํ•ด์ง‘๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ Tailwind๋Š” ๋” ๋งŽ์€ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ๋ฐฐ์›Œ์•ผ ํ•œ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์ง€๋งŒ, ๋‹ค๋ฅธ ์ธก๋ฉด์œผ๋กœ ๋ณด๋ฉด ์—„์ฒญ๋‚œ ์œ ์—ฐ์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ์ œ๊ณตํ•œ๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์–‘ํ•œ CSS ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์จ๋ณธ ์ €๋กœ์„œ๋Š” ์ด ๋ถ€๋ถ„์ด ๋งŽ์ด ๊ณต๊ฐ๋˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.

Tailwind๋ฅผ ๋‘˜๋Ÿฌ์‹ผ ์น˜์—ดํ•œ ๋…ผ์Ÿ

ํŠธ์œ„ํ„ฐ๊ฐ™์€๊ณณ์—์„œ๋Š” Tailwind๋ฅผ ์ข‹์•„ํ•˜๋Š” ๋ถ€๋ฅ˜์™€ ์‹ซ์–ดํ•˜๋Š” ๋ถ€๋ฅ˜๊ฐ€ ๋์—†๋Š” ๋…ผ์Ÿ์„.. ํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

Tailwind๋ฅผ ์ข‹์•„ํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋“ค์€ ์ƒ์‚ฐ์„ฑ ์ธก๋ฉด, ๋น ๋ฅด๊ฒŒ ๋””์ž์ธ์„ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋„์™€์ค€๋‹ค๋Š” ์ ์„ ๊ฐ•์กฐํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๋ณ€๊ฒฝ์ด๋‚˜ ์ˆ˜์ •์ด ์‰ฌ์›Œ์„œ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์ž˜ ๋œ๋‹ค๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค. ํ•œ ๋ฒˆ Tailwind๋ฅผ ์จ๋ณด๋ฉด ๋‹ค์‹œ๋Š” ๊ธฐ์กด์œผ๋กœ ๋Œ์•„๊ฐ€์ง€ ๋ชปํ•œ๋‹ค๋Š” ๊ฐ•๋ ฅํ•œ ์ฐฌ์„ฑ์˜ ์˜๊ฒฌ๋„ ๋ณด์˜€์Šต๋‹ˆ๋‹ค.

๋ฐ˜๋ฉด, Tailwind๋ฅผ ์‹ซ์–ดํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋“ค์€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๋ฅผ ๊ณผ๋„ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”๊ฒƒ์ด ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง€๋ฉฐ ์˜คํžˆ๋ ค ์œ ์ง€ ๋ณด์ˆ˜์„ฑ์ด ๋‚ฎ์•„์ง„๋‹ค๊ณ  ์ฃผ์žฅํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ณผ๋„ํ•˜๊ฒŒ Tailwind๋ฅผ ํ•™์Šตํ•  ๊ฒฝ์šฐ, css ๋ฐ”๋ณด๊ฐ€ ๋ ๊ฑฐ๋ผ๋Š” ๊ฒฝ๊ณ ๋„ ์กด์žฌํ–ˆ์Šต๋‹ˆ๋‹ค. (ํ•˜์ง€๋งŒ, Tailwind๋ฅผ ์‚ฌ์šฉํ•ด๋ณด์ง€ ์•Š์€ ์‚ฌ๋žŒ๋“ค์ด ์ด๋Ÿฐ ๋ง์„ ํ•œ๋‹ค๋Š” ์†์„ค๋„ ์žˆ์Šต๋‹ˆ๋‹ค.)

๋‚˜์˜ ์ฃผ๊ด€์ ์ธ ๋ฆฌ๋ทฐ

์ตœ๊ทผ ์ง„ํ–‰ํ•œ ํ”„๋กœ์ ํŠธ์—์„œ Tailwind๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋„์ž…ํ•ด ์‚ฌ์šฉํ•ด๋ณด์•˜๋Š”๋ฐ, ์ €์˜ ์ฃผ๊ด€์ ์ธ ์žฅ๋‹จ์ ์„ ๋ฆฌ๋ทฐํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์žฅ์ 

  • ๋งค์šฐ ๋น ๋ฅธ CSS ์ž‘์„ฑ์ด ๊ฐ€๋Šฅํ•˜๋‹ค (๊ธฐ์กด UI ์ž‘์—… ์‹œ๊ฐ„์˜ ํšจ์œจ์„ ์•ฝ 70% ์ •๋„ ์˜ฌ๋ ธ๋‹ค๊ณ  ์ƒ๊ฐ).
  • ๋‹ค๋ฅธ CSS ํ”„๋ ˆ์ž„์›Œํฌ๋ณด๋‹ค ์ž์œ ๋„๊ฐ€ ๋†’๊ณ , ์ˆ˜์ •์ด ์šฉ์ดํ•˜๋‹ค.
  • ์ปดํฌ๋„ŒํŠธ์˜ CSS ํŒŒ์ผ์„ ๋”ฐ๋กœ ์ฐพ์ง€ ์•Š์•„๋„ ๋˜์–ด ๊ฐ„ํŽธํ•˜๋‹ค.
  • ๋ชจ๋ฐ”์ผ ๋ฐ˜์‘ํ˜• ์ ์šฉ์ด ํŽธ๋ฆฌํ•˜๋‹ค (**sm:flex**์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜๋ฉด ์ž‘์€ ํ™”๋ฉด์—์„œ flex๊ฐ€ ์ ์šฉ๋จ).

๋‹จ์ 

  • ์ ์šฉํ•  CSS ์š”์†Œ๊ฐ€ ๋งŽ์„ ๊ฒฝ์šฐ, ์ธ๋ผ์ธ ์ฝ”๋“œ๊ฐ€ ์—„์ฒญ ๊ธธ์–ด์ง„๋‹ค.
  • ๋งŽ์€ ์–‘์˜ HTML ์š”์†Œ์™€ CSS๋ฅผ ํ•œ๊บผ๋ฒˆ์— ๋ณด๋ฉด ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง„๋‹ค.
  • **Vendor Prefixes** ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ถ”๊ฐ€์ ์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ install ํ•ด์•ผํ•œ๋‹ค.

๋งˆ์น˜๋ฉฐ

์‚ฌ์‹ค ์–ด๋–ค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋„์ž…ํ• ์ง€ ๊ฒฐ์ •ํ•  ๋•Œ์—๋Š” ๋งŽ์€ ๊ณ ๋ ค์‚ฌํ•ญ์„ ๋”ฐ์ ธ๋ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ์—๋„ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฒƒ์€ ์™ธ๋ถ€์ ์ธ ์š”์ธ(์ธ์ง€๋„, ์ปค๋ฎค๋‹ˆํ‹ฐ ์˜๊ฒฌ)์— ํœฉ์“ธ๋ฆฌ์ง€ ๋ง๊ณ  ์ž์‹ ์˜ ํ”„๋กœ์ ํŠธ์— ์ ํ•ฉํ•œ ๋„๊ตฌ์ธ์ง€ ํŒ๋‹จํ•˜๋Š” ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐ์ด ๋“ญ๋‹ˆ๋‹ค.

reference

https://tailwindcss.com/

https://tailwindcss.com/docs/browser-support#vendor-prefixes

https://kulkarniankita.com/newsletter/tailwind-vs-css-the-twitter-drama-

https://www.daleseo.com/tailwind/