์˜ค๋Š˜์€ ์ œ๊ฐ€ Context API์—์„œ Zustand๋กœ ์ƒํƒœ๊ด€๋ฆฌ ๋„๊ตฌ๋ฅผ ์ „ํ™˜ํ•˜๊ฒŒ ๋œ ๊ณผ์ •๊ณผ 2024๋…„ ํ˜„์žฌ ๋ฆฌ์•กํŠธ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์˜ ํ˜„ํ™ฉ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค.

 

1. Context API์˜ ํ•œ๊ณ„์™€ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ํ•„์š”์„ฑ

Context API๋กœ ์‹œ์ž‘ํ•œ ์ด์œ 

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

// ์ดˆ๊ธฐ Context API ์‚ฌ์šฉ ์˜ˆ์‹œ
const PetContext = React.createContext<PetContextType | undefined>(undefined);

export const PetProvider: React.FC<PropsWithChildren> = ({ children }) => {
  const [petData, setPetData] = useState<PetData>({
    allergies: [],
    symptoms: [],
    foodLog: []
  });

  return (
    <PetContext.Provider value={{ petData, setPetData }}>
      {children}
    </PetContext.Provider>
  );
};

 

ํ•œ๊ณ„์  ๋ฐœ๊ฒฌ

ํ•˜์ง€๋งŒ ํ”„๋กœ์ ํŠธ๊ฐ€ ์ปค์ง€๋ฉด์„œ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ํ•œ๊ณ„์ ์ด ๋“œ๋Ÿฌ๋‚˜๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค:

 

  • ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง: Context์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ํ•ด๋‹น Context๋ฅผ ๊ตฌ๋…ํ•˜๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋˜์—ˆ์Šต๋‹ˆ๋‹ค. 
  • ์ฝ”๋“œ ๋ณต์žก๋„ ์ฆ๊ฐ€: ์—ฌ๋Ÿฌ ๊ฐœ์˜ Context๋ฅผ ์ค‘์ฒฉํ•ด์„œ ์‚ฌ์šฉํ•˜๋‹ค ๋ณด๋‹ˆ Provider Hell์ด ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ƒํƒœ ์—…๋ฐ์ดํŠธ ๋กœ์ง ๊ด€๋ฆฌ์˜ ์–ด๋ ค์›€: ๋น„๋™๊ธฐ ์ž‘์—…๊ณผ ๋ณต์žกํ•œ ์ƒํƒœ ์—…๋ฐ์ดํŠธ ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ํ•œ๊ณ„๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

2. ์ฃผ์š” ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋น„๊ต ๋ถ„์„

Redux

Redux๋Š” ์˜ค๋žซ๋™์•ˆ ๋ฆฌ์•กํŠธ ์ƒํƒœ๊ณ„์˜ ํ‘œ์ค€๊ณผ ๊ฐ™์€ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž…๋‹ˆ๋‹ค.

Redux์˜ ํ•ต์‹ฌ ๊ฐœ๋…

 

  • Action: ์ƒํƒœ ๋ณ€๊ฒฝ์„ ์œ„ํ•œ ์ด๋ฒคํŠธ๋ฅผ ์„ค๋ช…ํ•˜๋Š” ๊ฐ์ฒด
  • Reducer: Action์„ ๋ฐ›์•„ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜
  • Store: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ „์ฒด ์ƒํƒœ๋ฅผ ๋ณด๊ด€ํ•˜๋Š” ๊ฐ์ฒด

Redux ๋ฏธ๋“ค์›จ์–ด ์ƒํƒœ๊ณ„

 

  • Redux Thunk: ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋ฏธ๋“ค์›จ์–ด
  • Redux Saga: ๋ณต์žกํ•œ ๋น„๋™๊ธฐ ํ๋ฆ„์„ ์ œ์–ดํ•˜๊ธฐ ์œ„ํ•œ ๋ฏธ๋“ค์›จ์–ด
    • Generator ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ ์„ ์–ธ์  ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ
    • ๋ณต์žกํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค(API ์š”์ฒญ ์ทจ์†Œ, ์žฌ์‹œ๋„ ๋“ฑ) ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
  • Redux Toolkit: ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•œ ๊ณต์‹ ๋„๊ตฌ

๊ทธ๋Ÿฌ๋‚˜ Redux๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค:

 

  • ๋งŽ์€ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ
  • ํ•™์Šต ๊ณก์„ ์ด ๊ฐ€ํŒŒ๋ฆ„
  • ์ž‘์€ ์ƒํƒœ ๋ณ€ํ™”์—๋„ ๋งŽ์€ ์ฝ”๋“œ ํ•„์š”

Recoil

Facebook์—์„œ ๋งŒ๋“  ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

 

์žฅ์ 

  • React์™€์˜ ๋†’์€ ํ˜ธํ™˜์„ฑ
  • ๊ฐ„๋‹จํ•œ API (atom, selector)
  • ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๊ฐ€ ์‰ฌ์›€

๋‹จ์ 

  • ์•„ํ†ฐ์œผ๋กœ ๊ด€๋ฆฌ๋˜๋Š” ์ „์—ญ ์ƒํƒœ๋ฅผ ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๋ผ๋„ ๋ฐ”๋กœ ์•„ํ†ฐ์„ ๊ตฌ๋…ํ•ด์„œ ์—…๋ฐ์ดํŠธ๋ฅผ ๋ฐ›๊ธฐ์—, ์˜์กด์„ฑ์ด ์—ฌ๋Ÿฌ ๋ฐฉํ–ฅ์œผ๋กœ ์—ฎ์ด๋ฉด์„œ ์˜ˆ์ธกํ•˜๊ธฐ ํž˜๋“ค์–ด์ง‘๋‹ˆ๋‹ค.

Zustand

๊ฐ€๋ณ๊ณ  ์ง๊ด€์ ์ธ API๋ฅผ ์ œ๊ณตํ•˜๋Š” ํ˜„๋Œ€์ ์ธ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

 

์ฃผ์š” ํŠน์ง•

  • ์ตœ์†Œํ•œ์˜ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ
  • TypeScript ์ง€์›์ด ํ›Œ๋ฅญํ•จ
  • Redux DevTools ์ง€์›
  • ๋ฏธ๋“ค์›จ์–ด ์‹œ์Šคํ…œ (persist, devtools ๋“ฑ)

์˜ˆ์‹œ ์ฝ”๋“œ:

import create from 'zustand'

interface PetStore {
  symptoms: Symptom[]
  addSymptom: (symptom: Symptom) => void
  foodLogs: FoodLog[]
  addFoodLog: (log: FoodLog) => void
}

const usePetStore = create<PetStore>((set) => ({
  symptoms: [],
  addSymptom: (symptom) => set((state) => ({
    symptoms: [...state.symptoms, symptom]
  })),
  foodLogs: [],
  addFoodLog: (log) => set((state) => ({
    foodLogs: [...state.foodLogs, log]
  }))
}))

 

3. ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ์˜ ์„ ํƒ: Zustand

์•Œ๋ฆฌ๋‹ˆ ํ”„๋กœ์ ํŠธ์—์„œ์˜ ์š”๊ตฌ์‚ฌํ•ญ

  1. ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ
  2. ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ (์•Œ๋ ˆ๋ฅด๊ธฐ ์ฆ์ƒ, ์Œ์‹ ๊ธฐ๋ก, ๋ถ„์„ ๋ฐ์ดํ„ฐ)
  3. ์„ฑ๋Šฅ ์ตœ์ ํ™”
  4. TypeScript ์ง€์›

Zustand ์„ ํƒ ์ด์œ 

  1. ๊ฐ„๋‹จํ•œ ์„ค์ •๊ณผ ์‚ฌ์šฉ๋ฒ•: ๋ณ„๋„์˜ Provider ์„ค์ •์ด ํ•„์š” ์—†๊ณ , ์ง๊ด€์ ์ธ API๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  2. ์„ฑ๋Šฅ ์ตœ์ ํ™”: ํ•„์š”ํ•œ ์ƒํƒœ๋งŒ ๊ตฌ๋…ํ•˜์—ฌ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  3. TypeScript ์ง€์›: ํƒ€์ž… ์ถ”๋ก ์ด ์ž˜ ๋™์ž‘ํ•˜์—ฌ ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์ด ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  4. ๋ฏธ๋“ค์›จ์–ด ์ง€์›: persist ๋ฏธ๋“ค์›จ์–ด๋ฅผ ํ†ตํ•ด ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€ ์—ฐ๋™์ด ์‰ฌ์› ์Šต๋‹ˆ๋‹ค.

Context API์™€์˜ ์กฐํ™”

Context API์™€ Zustand๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์—ญํ• ์„ ๊ตฌ๋ถ„ํ–ˆ์Šต๋‹ˆ๋‹ค:

  • Context API: ํ…Œ๋งˆ, ์ธ์ฆ ๋“ฑ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ „๋ฐ˜์ ์ธ ์„ค์ •๊ณผ ๊ด€๋ จ๋œ ์ƒํƒœ ๊ด€๋ฆฌ (๋‹จ์–ด ๊ทธ๋Œ€๋กœ ํŠน์ • ๋งฅ๋ฝ์„ ์”Œ์šธ๋•Œ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.)
  • Zustand: ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๊ด€๋ จ๋œ ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ

 

4. ๊ฒฐ๋ก 

2024๋…„ ํ˜„์žฌ, ์ €๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ์ค€์œผ๋กœ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค:

  1. ์ž‘์€ ํ”„๋กœ์ ํŠธ: Context API๋งŒ์œผ๋กœ๋„ ์ถฉ๋ถ„
  2. ์ค‘๊ฐ„ ๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ: Zustand
  3. ๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ: Redux Toolkit ๋˜๋Š” Zustand

์•Œ๋ฆฌ๋‹ˆ์™€ ๊ฐ™์ด ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์™€ ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ํ”„๋กœ์ ํŠธ์—๋Š” Zustand๊ฐ€ ํŠนํžˆ ์ ํ•ฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ๊ฐ€ ๊ฐ„๊ฒฐํ•˜๋ฉด์„œ๋„ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋ฉฐ, ํŠนํžˆ TypeScript์™€์˜ ํ˜ธํ™˜์„ฑ์ด ๋›ฐ์–ด๋‚˜ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์ด ๋งค์šฐ ์ข‹์•˜์Šต๋‹ˆ๋‹ค.

 

 

 

 

ํ”„๋ฆฌํ”ฝ์Šค!!

 

ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์„ ํ•˜๋‹ค ๋ณด๋ฉด ํฌ๋กœ์Šค๋ธŒ๋ผ์šฐ์ง• ์ด์Šˆ๋Š” ํ”ผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

::-webkit-input-placeholder์™€ ๊ฐ™์€ ๋ธŒ๋ผ์šฐ์ €๋ณ„ ํ”„๋ฆฌํ”ฝ์Šค(-webkit-, -moz-, -ms- ๋“ฑ)๋ฅผ ์ผ์ผ์ด ์ถ”๊ฐ€ํ•˜๋Š” ๊ฑด ๊ต‰์žฅํžˆ ๋ฒˆ๊ฑฐ๋กœ์šด ์ž‘์—…์ด์˜ˆ์š”.

 

์‚ฌ์‹ค ํ˜„์—…์—์„œ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์ด๋Ÿฌํ•œ ํ”„๋ฆฌํ”ฝ์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ๋งค์šฐ ๊ณค๋ž€ํ•œ ์ผ์ž…๋‹ˆ๋‹ค.

(์ €๋„ ๊ด€๋ จ๋œ ๋Œ€ํ™”๋ฅผ ๋‚˜๋ˆ„๋‹ค ์•Œ๊ฒŒ๋์–ด์š”!)

 

์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ฒˆ๋“ค๋Ÿฌ๋ฅผ ํ†ตํ•ด ์ž๋™์œผ๋กœ ํ”„๋ฆฌํ”ฝ์Šค๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” ๋ฐฉ๋ฒ•์ธ Autoprefixer๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

PostCSS๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ผ๋ฉด, ํ˜น์€ CSS๋กœ ๋ณ€ํ™˜ํ•œ ํ›„ postcss-loader๊ฐ€ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ํ•œ๋‹ค๋ฉด ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ฐ ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ์Šคํƒ€์ผ์„ ๋”ฐ๋กœ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ Autoprefixer๊ฐ€ ์›นํ‚ท ํ”„๋ฆฌํ”ฝ์Šค์™€ ๊ฐ™์€ ๊ฒƒ์„ ์ž๋™์œผ๋กœ ๋ถ™์—ฌ์ค๋‹ˆ๋‹ค.

 

์ด๋ฒˆ ํฌ์ŠคํŠธ์—์„œ๋Š” PostCSS์™€ Autoprefixer๋ฅผ ํ™œ์šฉํ•ด ํ•ด๋‹น ISSUE๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‹ค์ œ ํ”„๋กœ์ ํŠธ ๊ฒฝํ—˜์„ ๋ฐ”ํƒ•์œผ๋กœ ์ƒ์„ธํžˆ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!

 

PostCSS์™€ Autoprefixer๋ž€?

PostCSS

  • JavaScript ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•ด CSS๋ฅผ ๋ณ€ํ™˜ํ•˜๋Š” ๋„๊ตฌ
  • "Post-processor"๋ผ๊ณ ๋„ ๋ถˆ๋ฆฌ๋Š” ์ด์œ ๋Š” CSS๋ฅผ ์ž‘์„ฑํ•œ ํ›„์— ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ
  • CSS ๊ตฌ๋ฌธ ๋ถ„์„์— ์ตœ์ ํ™”๋œ ํŒŒ์„œ๋ฅผ ์‚ฌ์šฉํ•ด ๋†’์€ ์„ฑ๋Šฅ์„ ๋ณด์ž„
  • CSS ์ „์ฒ˜๋ฆฌ๊ธฐ(Sass)๋‚˜ ํ›„์ฒ˜๋ฆฌ๊ธฐ๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ (์ด ๋ถ€๋ถ„์€ ์ œ๊ฐ€ ํ—ท๊ฐˆ๋ ธ๋˜ ๋ถ€๋ถ„์ด๋ผ ์•„๋ž˜ ์ถ”๊ฐ€์„ค๋ช…์„ ๋„ฃ์—ˆ์–ด์š”!)
    1. CSS ์ „์ฒ˜๋ฆฌ๊ธฐ(SASS):
      CSS ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•œ ์–ธ์–ด ์ž…๋‹ˆ๋‹ค. ๋ณ€์ˆ˜, ์ค‘์ฒฉ ๊ทœ์น™, ๋ฏน์Šค์ธ ๋“ฑ์„ ์ง€์›ํ•˜๊ณ , PostCSS๋Š” Sass๋กœ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, PostCSS ์ž์ฒด๋Š” Sass ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜์ง„ ์•Š์•„์š”.
    2. CSS ํ›„์ฒ˜๋ฆฌ๊ธฐ:
      PostCSS๋Š” CSS์ฝ”๋“œ๊ฐ€ ์ž‘์„ฑ๋œ ํ›„์— ์ถ”๊ฐ€์ ์ธ ๋ณ€ํ™˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋„๊ตฌ๋กœ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      ์˜ˆ๋ฅผ ๋“ค์–ด, ์ œ๊ฐ€ ์ง€๊ธˆ ์ž‘์„ฑํ•˜๋Š” Autoprefixer ๊ฐ™์€ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•ด์„œ ์ž๋™์œผ๋กœ ๋ธŒ๋ผ์šฐ์ € ํ”„๋ฆฌํ”ฝ์Šค๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ์š”!
  • 200๊ฐœ ์ด์ƒ์˜ ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ ์ œ๊ณต (์ƒํƒœ๊ณ„๊ฐ€ ํฝ๋‹ˆ๋‹ค!)
  • ๋ชจ๋“ˆํ™”๋œ ๊ตฌ์กฐ๋กœ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ๋งŒ ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ ์„ ํƒ์  ์ถ”๊ฐ€ ๊ฐ€๋Šฅ

Autoprefixer

  • PostCSS์˜ ๋Œ€ํ‘œ์ ์ธ ํ”Œ๋Ÿฌ๊ทธ์ธ
  • ๋ธŒ๋ผ์šฐ์ € ํ”„๋ฆฌํ”ฝ์Šค๋ฅผ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” ๋„๊ตฌ
  • Browserslist ์„ค์ •์„ ํ†ตํ•ด ์ง€์› ๋ธŒ๋ผ์šฐ์ € ๋ฒ”์œ„ ์ง€์ • ๊ฐ€๋Šฅ

 

Create React App๊ณผ Vite์˜ ๊ธฐ๋ณธ ์ง€์›

ํ•ด๋‹น ๋ถ€๋ถ„์€ Webpack์„ ์ง์ ‘ ์„ค์ •ํ•˜์ง€ ์•Š์„ ๋• ์–ด๋–ป๊ฒŒ ๋˜๋Š” ๊ฑด์ง€, ๊ธฐ๋ณธ ์„ค์ •์ด ๋˜์–ด ์žˆ๋Š”์ง€ ๋“ฑ์ด ๊ถ๊ธˆํ•ด ์ฐพ์•„๋ณด๊ฒŒ ๋์Šต๋‹ˆ๋‹ค.

Create React App (CRA)

  • PostCSS์™€ Autoprefixer๊ฐ€ ๊ธฐ๋ณธ์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ์Œ
  • ๋ณ„๋„ ์„ค์ • ์—†์ด ๋ฐ”๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • browserslist ์„ค์ •๋„ ๊ธฐ๋ณธ ์ œ๊ณต

CRA์˜ package.json์„ ๋ณด์‹œ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

"browserslist": {
  "production": [
    ">0.2%",
    "not dead",
    "not op_mini all"
  ],
  "development": [
    "last 1 chrome version",
    "last 1 firefox version",
    "last 1 safari version"
  ]
}


Vite

  • PostCSS๋Š” ๊ธฐ๋ณธ ์ง€์›
  • Autoprefixer๋Š” ๋ณ„๋„ ์„ค์น˜ ํ•„์š”
yarn add -D autoprefixer
// vite.config.js
import autoprefixer from 'autoprefixer'

export default {
  css: {
    postcss: {
      plugins: [
        autoprefixer()
      ]
    }
  }
}

 

ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์— Custom Webpack ์„ค์ •ํ•˜๊ธฐ

์ปค์Šคํ…€ Webpack ์„ค์ •์—์„œ๋Š” ์ˆ˜๋™์œผ๋กœ PostCSS์™€ Autoprefixer๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ•„์š”ํ•œ ํŒจํ‚ค์ง€ ์„ค์น˜

yarn add -D postcss postcss-loader autoprefixer


์›นํŒฉ ์„ค์ • ์˜ˆ์‹œ

module.exports = {
  module: {
    rules: [
      {
        test: /\.module\.scss$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              modules: {
                localIdentName: "[name]__[local]--[hash:base64:5]",
              },
              importLoaders: 2,
            },
          },
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  ["autoprefixer"]
                ],
              },
            },
          },
          "sass-loader",
        ],
      },
      {
        test: /\.(scss|css)$/,
        exclude: /\.module\.scss$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              importLoaders: 2,
            },
          },
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  ["autoprefixer"]
                ],
              },
            },
          },
          "sass-loader",
        ],
      },
    ],
  },
  // ... ๊ธฐํƒ€ ์›นํŒฉ ์„ค์ •
};

 

์ถ”๊ฐ€์ ์œผ๋กœ ์ €๋Š” ์„ค์ •ํ•˜์ง€ ์•Š์•˜์ง€๋งŒ ๋‹ค๋ฅธ ๋ธ”๋กœ๊ทธ ๊ธ€์„ ๋ณด์‹œ๋ฉด ๊ฐ„ํ˜น postcss-scss์— ๋Œ€ํ•œ ์ถ”๊ฐ€๊ฐ€ ๋ณด์ด์…จ์„ ํ…๋ฐ, ์ด๊ฑด ๋ฌด์Šจ ์—ญํ• ์ผ๊นŒ์š”?!

postcss-scss๋ž€?

  • ์ผ๋ฐ˜์ ์œผ๋กœ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค
  • sass-loader๊ฐ€ SCSS๋ฅผ CSS๋กœ ๋ณ€ํ™˜ํ•œ ํ›„ postcss-loader๊ฐ€ ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ
  • postcss-scss๋Š” SCSS ๊ตฌ๋ฌธ์„ ์ง์ ‘ PostCSS ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์„ ๋•Œ๋งŒ ํ•„์š”

 

์‹ค์ˆ˜ํ–ˆ๋˜ ๋ถ€๋ถ„๊ณผ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

package.json์— ์ž‘์„ฑ๋ผ์•ผ ํ•˜๋Š” json ๋ฌธ๋ฒ•์„. browserslistrc ํŒŒ์ผ์— ์ ์šฉํ•ด ๋ฒ„๋ ธ์–ด์š”. ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฝ๋‹ค ์ˆœ๊ฐ„์ ์œผ๋กœ ์ฐฉ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋„ˆ๋ฌด ๊ฐ„๋‹จํ•˜์ง€๋งŒ ์ œ ๊ธฐ์ค€์—์„œ ์น˜๋ช…์ ์ธ ์‹ค์ˆ˜๋ž€ ์ƒ๊ฐ์ด ๋“ค์–ด ๊ธฐ๋กํ•ด๋‘๋ ค ํ•ฉ๋‹ˆ๋‹ค!

Browserslist ์„ค์ • ์˜ค๋ฅ˜

Error [BrowserslistError]: Unknown browser query `{`. Maybe you are using old Browserslist or made typo in query.

 

 

.browserslistrc ํŒŒ์ผ ์‚ฌ์šฉ (๊ถŒ์žฅ)

์ €๋Š” ์ตœ์ข…์ ์œผ๋กœ ์•„๋ž˜์˜ ์ฝ”๋“œ๋กœ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค!

# ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ
[production]
>0.2% # ์ „ ์„ธ๊ณ„ ์‚ฌ์šฉ๋ฅ  0.2% ์ด์ƒ์ธ ๋ธŒ๋ผ์šฐ์ €
not dead # ๊ณต์‹ ์ง€์›์ด ์ค‘๋‹จ๋˜์ง€ ์•Š์€ ๋ธŒ๋ผ์šฐ์ €
not op_mini all # Opera Mini ๋ธŒ๋ผ์šฐ์ € ์ œ์™ธ

# ๊ฐœ๋ฐœ ํ™˜๊ฒฝ
[development]
last 1 chrome version # ์ตœ์‹  ํฌ๋กฌ ๋ฒ„์ „
last 1 firefox version # ์ตœ์‹  ํŒŒ์ด์–ดํญ์Šค ๋ฒ„์ „
last 1 safari version # ์ตœ์‹  ์‚ฌํŒŒ๋ฆฌ ๋ฒ„์ „


ํ•ด๊ฒฐ๋ฐฉ๋ฒ• 2: package.json์— ์„ค์ •

{
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}


Browserslist ์„ค์ • ๊ฐ€์ด๋“œ

์ฃผ์š” ์ฟผ๋ฆฌ ์„ค๋ช…

  • >0.2%: ์ „ ์„ธ๊ณ„ ์‚ฌ์šฉ๋ฅ  0.2% ์ด์ƒ์ธ ๋ธŒ๋ผ์šฐ์ €
  • not dead: ๊ณต์‹ ์ง€์›์ด ์ค‘๋‹จ๋˜์ง€ ์•Š์€ ๋ธŒ๋ผ์šฐ์ €
  • not op_mini all: Opera Mini ๋ธŒ๋ผ์šฐ์ € ์ œ์™ธ
  • last 1 chrome version: ์ตœ์‹  ํฌ๋กฌ ๋ฒ„์ „
  • last 2 versions: ๊ฐ ๋ธŒ๋ผ์šฐ์ €์˜ ์ตœ์‹  2๊ฐœ ๋ฒ„์ „
  • > 1%: ์ „ ์„ธ๊ณ„ ์ ์œ ์œจ 1% ์ด์ƒ
  • IE 11: IE 11 ์ง€์›

์„ค์ • ํ…Œ์ŠคํŠธ

  • browserslist.dev์—์„œ ์„ค์ •์„ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Œ
  • ์–ด๋–ค ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํฌํ•จ๋˜๋Š”์ง€ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธ ๊ฐ€๋Šฅ

 

์‹ค์ œ ๋™์ž‘ ์˜ˆ์‹œ

๋ณ€ํ™˜ ์ „ CSS

.example {
  display: flex;
  user-select: none;
}

๋ณ€ํ™˜ ํ›„ CSS

.example {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-user-select: none;
     -moz-user-select: none;
      -ms-user-select: none;
          user-select: none;
}

 

์ฃผ์˜์‚ฌํ•ญ๊ณผ ํŒ!

  • ๋กœ๋” ์ˆœ์„œ: PostCSS ๋กœ๋”๋Š” Sass ๋กœ๋” ์ดํ›„, CSS ๋กœ๋” ์ด์ „์— ์œ„์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.  
    1. sass-loader (SCSS -> CSS ๋ณ€ํ™˜)
    2. postcss-loader (์ž๋™ ํ”„๋ฆฌํ”ฝ์Šค ์ถ”๊ฐ€)
    3. css-loader (CSS -> JS ๋ณ€ํ™˜)
    4. style-loader (JS -> style ํƒœ๊ทธ ์‚ฝ์ž…)
  • ์„ฑ๋Šฅ ์ตœ์ ํ™”: development ํ™˜๊ฒฝ์—์„œ๋Š” source map์„ ํ™œ์„ฑํ™”ํ•˜๊ณ , production์—์„œ๋Š” ๋น„ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
  • ๋ธŒ๋ผ์šฐ์ € ์ง€์› ๋ฒ”์œ„: browserslist ์„ค์ •์„ ํ†ตํ•ด ์ง€์›ํ•  ๋ธŒ๋ผ์šฐ์ € ๋ฒ”์œ„๋ฅผ ์ ์ ˆํžˆ ์กฐ์ ˆํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

์ฃผ์˜์‚ฌํ•ญ๊ณผ ํŒ์—์„œ์˜ Development ํ™˜๊ฒฝ์—์„œ source map ํ™œ์„ฑํ™”ํ•˜๋Š” ์ด์œ  & Production ํ™˜๊ฒฝ์—์„œ ๋น„ํ™œ์„ฑํ™” ํ•˜๋Š” ์ด์œ ์— ๋Œ€ํ•ด์„œ๋งŒ ์ข€ ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณผ๊ฒŒ์š”!

 

Development ํ™˜๊ฒฝ์—์„œ source map ํ™œ์„ฑํ™”ํ•˜๋Š” ์ด์œ :

  1. ๋””๋ฒ„๊น… ์šฉ์ด์„ฑ
    • ๊ฐœ๋ฐœ ์ค‘์—๋Š” ์›๋ณธ ์†Œ์Šค ์ฝ”๋“œ์™€ ๋ณ€ํ™˜๋œ ์ฝ”๋“œ๋ฅผ ๋งคํ•‘ํ•˜์—ฌ ์‰ฝ๊ฒŒ ๋””๋ฒ„๊น…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
    • ๋ธŒ๋ผ์šฐ์ €์˜ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ ์‹ค์ œ ์ž‘์„ฑํ•œ CSS ํŒŒ์ผ์˜ ์œ„์น˜์™€ ๋ผ์ธ์„ ์ •ํ™•ํžˆ ํ™•์ธ ๊ฐ€๋Šฅ
    • PostCSS๋กœ ๋ณ€ํ™˜๋œ ์ฝ”๋“œ๊ฐ€ ์•„๋‹Œ ์›๋ณธ ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ํ™•์ธํ•˜๋ฉฐ ์ˆ˜์ • ๊ฐ€๋Šฅ
  2. ๊ฐœ๋ฐœ ํšจ์œจ์„ฑ
    • ์ฝ”๋“œ ์ˆ˜์ • ์‹œ ์ฆ‰๊ฐ์ ์ธ ํ”ผ๋“œ๋ฐฑ ํ™•์ธ ๊ฐ€๋Šฅ
    • CSS ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ์ •ํ™•ํ•œ ์œ„์น˜ ์ถ”์ ์ด ๊ฐ€๋Šฅํ•˜์—ฌ ์ˆ˜์ • ์‹œ๊ฐ„ ๋‹จ์ถ•
    • ๋ณต์žกํ•œ ์Šคํƒ€์ผ ๊ตฌ์กฐ์—์„œ๋„ ์›๋ณธ ์ฝ”๋“œ ์œ„์น˜๋ฅผ ์‰ฝ๊ฒŒ ํŒŒ์•…!

Production ํ™˜๊ฒฝ์—์„œ source map ๋น„ํ™œ์„ฑํ™”ํ•˜๋Š” ์ด์œ :

  1. ํŒŒ์ผ ํฌ๊ธฐ ์ตœ์ ํ™”
    • source map ํŒŒ์ผ์€ ์›๋ณธ ์ฝ”๋“œ์™€ ๋ณ€ํ™˜๋œ ์ฝ”๋“œ์˜ ๋งคํ•‘ ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๋ฏ€๋กœ ์ƒ๋‹นํ•œ ํฌ๊ธฐ๋ฅผ ์ฐจ์ง€
    • ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์ „์†ก์„ ์ค„์—ฌ ๋กœ๋”ฉ ์†๋„ ํ–ฅ์ƒ
    • ์ผ๋ฐ˜์ ์œผ๋กœ source map ํŒŒ์ผ์€ ์›๋ณธ ํŒŒ์ผ ํฌ๊ธฐ์˜ ์ ˆ๋ฐ˜ ์ด์ƒ์„ ์ฐจ์ง€ํ•  ์ˆ˜ ์žˆ์Œ
  2. ๋ณด์•ˆ ๊ฐ•ํ™”
    • source map์ด ๋…ธ์ถœ๋˜๋ฉด ์›๋ณธ ์ฝ”๋“œ ๊ตฌ์กฐ๊ฐ€ ๋“œ๋Ÿฌ๋‚  ์ˆ˜ ์žˆ์Œ
    • ์•…์˜์ ๋ฅผ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž๊ฐ€ ์ฝ”๋“œ ๊ตฌ์กฐ๋ฅผ ๋ถ„์„ํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€
    • ๊ธฐ์—…์˜ proprietary ์ฝ”๋“œ ๋ณดํ˜ธ!!
  3. ์„ฑ๋Šฅ ์ตœ์ ํ™”
    • ๋ธŒ๋ผ์šฐ์ €๊ฐ€ source map์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์ถ”๊ฐ€์ ์ธ ์ž‘์—… ์ œ๊ฑฐ
    • ์ดˆ๊ธฐ ํŽ˜์ด์ง€ ๋กœ๋”ฉ ์‹œ๊ฐ„ ๋‹จ์ถ•
    • ์„œ๋ฒ„ ๋Œ€์—ญํญ ์‚ฌ์šฉ๋Ÿ‰ ๊ฐ์†Œ

์‹ค์ œ ์„ค์ • ์˜ˆ์‹œ! (ํ”„๋กœ์ ํŠธ ์ฝ”๋“œ๋ณด๋‹ค ๊ฐ„๋žตํ™”)

// webpack.config.js
module.exports = {
  mode: process.env.NODE_ENV,
  module: {
    rules: [{
      test: /\.css$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            sourceMap: process.env.NODE_ENV === 'development'
          }
        },
        {
          loader: 'postcss-loader',
          options: {
            sourceMap: process.env.NODE_ENV === 'development'
          }
        }
      ]
    }]
  }
};

 

 

๊ฒฐ๋ก 

PostCSS์™€ Autoprefixer๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํฌ๋กœ์Šค๋ธŒ๋ผ์šฐ์ง•์„ ์œ„ํ•œ ์ˆ˜๋™ ์ž‘์—…์„ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

CRA๋‚˜ Create Vite๊ฐ™์€ ๊ฒฝ์šฐ ์ด๋ฏธ ์ด๋Ÿฌํ•œ ๊ธฐ๋Šฅ์„ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ์ปค์Šคํ…€ ์›นํŒฉ ์„ค์ •์—์„œ๋„ ๊ฐ„๋‹จํ•œ ์„ค์ •๋งŒ์œผ๋กœ ๋™์ผํ•œ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

ํŠนํžˆ Browserslist ์„ค์ •์„ ํ†ตํ•ด ํ”„๋กœ์ ํŠธ์˜ ๋ธŒ๋ผ์šฐ์ € ์ง€์› ๋ฒ”์œ„๋ฅผ ๋ช…ํ™•ํžˆ ํ•˜๊ณ , ํ•„์š”ํ•œ ํ”„๋ฆฌํ”ฝ์Šค๋งŒ ์ถ”๊ฐ€ํ•จ์œผ๋กœ์จ ์ตœ์ ํ™”๋œ CSS๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๊ฒ ์ฃ ?!

 

.browserslistrc ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜๋ฉด ์„ค์ • ๊ด€๋ฆฌ๊ฐ€ ๋”์šฑ ์šฉ์ดํ•ด์ง€๋ฉฐ, ๋‹ค๋ฅธ ๋„๊ตฌ๋“ค๊ณผ๋„ ์„ค์ •์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์—ฌ๊ธฐ์„œ "๋‹ค๋ฅธ ๋„๊ตฌ๋“ค"์€ ์ฃผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ CSS ๋˜๋Š” JavaScript ๋„๊ตฌ๋“ค์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค!

  1. Autoprefixer: CSS์— ํ•„์š”ํ•œ ๋ธŒ๋ผ์šฐ์ € ํ”„๋ฆฌํ”ฝ์Šค๋ฅผ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” ๋„๊ตฌ๋กœ, Browserslist ์„ค์ •์„ ํ†ตํ•ด ์–ด๋–ค ํ”„๋ฆฌํ”ฝ์Šค๋ฅผ ์ถ”๊ฐ€ํ• ์ง€ ๊ฒฐ์ • (์ง€๊ธˆ๊นŒ์ง€ ์„ค๋ช…๋“œ๋ฆฐ ๋‚ด์šฉ์ด์ฃ ?!)
  2. Babel: ์ตœ์‹  JavaScript ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜• ๋ธŒ๋ผ์šฐ์ €์—์„œ๋„ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๋„๊ตฌ๋กœ, Browserslist ์„ค์ •์„ ํ†ตํ•ด ์ง€์›ํ•  ๋ธŒ๋ผ์šฐ์ € ์ •์˜ ๊ฐ€๋Šฅ
  3. ESLint: JavaScript ์ฝ”๋“œ์˜ ํ’ˆ์งˆ์„ ๊ด€๋ฆฌํ•˜๋Š” ๋„๊ตฌ๋กœ, Browserslist์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด ํŠน์ • ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ์— ๋งž๋Š” ์ฝ”๋“œ ์Šคํƒ€์ผ ์ ์šฉ ๊ฐ€๋Šฅ
  4. Stylelint: CSS ์Šคํƒ€์ผ์„ ๊ฒ€์‚ฌํ•˜๋Š” ๋„๊ตฌ๋กœ, Browserslist ์„ค์ •์„ ์ฐธ๊ณ ํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ € ํ˜ธํ™˜์„ฑ ๊ด€๋ จ ๊ทœ์น™ ์ ์šฉ ๊ฐ€๋Šฅ

์ฆ‰, .browserslistrc ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋Ÿฌํ•œ ๋‹ค์–‘ํ•œ ๋„๊ตฌ๋“ค์ด ๋™์ผํ•œ ๋ธŒ๋ผ์šฐ์ € ์ง€์› ๋ฒ”์œ„๋ฅผ ๊ณต์œ ํ•˜๊ฒŒ ๋˜์–ด, ๊ฐœ๋ฐœ ๋ฐ ์œ ์ง€๋ณด์ˆ˜ ๊ณผ์ •์—์„œ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

 

 

๐Ÿ“ƒ ์ฐธ๊ณ  ๋ฌธํ—Œ  
PostCSS ๊ณต์‹ ๋ฌธ์„œ
Autoprefixer GitHub
Browserslist
Create React App PostCSS ์„ค์ •
Vite CSS ์„ค์ •
Webpack 5 postcss-loader 
Browserslist ๊ด€๋ จ ๊ฐœ์ธ ๋ธ”๋กœ๊ทธ

 

express-openapi-validator

 

์•ˆ๋…•ํ•˜์„ธ์š”! express-openapi-validator์˜ HTTP ์ธ์ฆ ๊ด€๋ จ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด PR์„ ์ œ์ถœํ–ˆ๋˜ ๊ณผ์ •์„ ๊ณต์œ ๋“œ๋ฆฌ๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ด์ „ ํฌ์ŠคํŠธ์—์„œ express-openapi-validator๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๋ฐœ๊ฒฌํ•œ ์ธ์ฆ ๊ด€๋ จ ์ด์Šˆ๋ฅผ ๋‹ค๋ค˜์—ˆ๋Š”๋ฐ์š”, ๊ฐ„๋‹จํžˆ ์š”์•ฝ๋“œ๋ฆฌ๋ฉด:

Express.js๋กœ REST API๋ฅผ ๊ตฌ์ถ•ํ•˜๋ฉฐ JWT๋ฅผ http-only ์ฟ ํ‚ค๋กœ ์ „๋‹ฌํ•˜๋ ค ํ–ˆ๋Š”๋ฐ,
Authorization ํ—ค๋”์™€ CSRF ํ† ํฐ ๊ฒ€์ฆ์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

ํŠนํžˆ validateHttp() ๋ฉ”์„œ๋“œ๊ฐ€ Authorization ํ—ค๋”๋ฅผ ํ•„์ˆ˜๋กœ ์š”๊ตฌํ•˜๋Š” ๋ฐ”๋žŒ์—,
์ฟ ํ‚ค๋กœ JWT๋ฅผ ์ „๋‹ฌํ•˜๋Š” ์ƒํ™ฉ์—์„œ๋„ ํ—ค๋”๊ฐ€ ํ•„์š”ํ•œ ์ƒํ™ฉ์ด ๋์ฃ .


์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•ด๋ณด๋‹ˆ SecuritySchemes์™€ AuthValidator ํด๋ž˜์Šค์˜ validateHttp ๋ฉ”์„œ๋“œ์—์„œ ์ด ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ์—ˆ๊ณ ,
type: http๋กœ ์„ค์ •๋œ ์Šคํ‚ด์—์„œ Authorization ํ—ค๋”๋ฅผ ๊ฐ•์ œํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Pull Request๋ฅผ ์ œ์ถœํ–ˆ๊ณ , ์—ฌ๋Ÿฌ ๋ฒˆ์˜ ์ฝ”๋“œ ๋ฆฌ๋ทฐ์™€ ์ˆ˜์ • ๊ณผ์ •์„ ๊ฑฐ์ณ ๋งˆ์นจ๋‚ด Merge๊ฐ€ ๋˜์–ด express-openapi-validator์˜ Contributor๊ฐ€ ๋œ ์—ฌ์ •์„ ์˜ค๋Š˜ ์ƒ์„ธํžˆ ๊ณต์œ ๋“œ๋ฆฌ๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค! ๐ŸŽ‰

๐ŸŽฏ ์ฒซ ๋ฒˆ์งธ ์‹œ๋„: ์ฟ ํ‚ค ์ง€์› ์ถ”๊ฐ€

์ฒ˜์Œ์—๋Š” ๋‹จ์ˆœํžˆ Authorization ํ—ค๋” ์™ธ์—๋„ ์ฟ ํ‚ค๋ฅผ ํ†ตํ•œ ์ธ์ฆ์„ ์ง€์›ํ•˜๋„๋ก ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค:

if (!authHeader && !authCookie) {
  throw Error(`Authorization header or cookie required`);
}

if (type === 'bearer') {
  if (authHeader && !authHeader.includes('bearer')) {
    throw Error(`Authorization header with scheme 'Bearer' required`);
  }

  if (!authHeader && authCookie === undefined) {
    throw Error(`Bearer token required in authorization header or cookie`);
  }
}

๐Ÿ’ก ์ฝ”๋“œ ๋ฆฌ๋ทฐ ํ”ผ๋“œ๋ฐฑ

์ œ๊ฐ€ ์ œ์ถœํ•œ PR์— ๋Œ€ํ•ด ๋ฉ”์ธํ…Œ์ด๋„ˆ๊ป˜์„œ ์ค‘์š”ํ•œ ํ”ผ๋“œ๋ฐฑ์„ ์ฃผ์…จ์Šต๋‹ˆ๋‹ค:

  1. OpenAPI ๋ช…์„ธ์— ๋”ฐ๋ฅด๋ฉด, in: cookie๋กœ ์ง€์ •๋œ ๊ฒฝ์šฐ์™€ ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ๋ฅผ ๊ตฌ๋ถ„ํ•ด์•ผ ํ•จ
  2. ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋„ ๊ฐ๊ฐ์˜ ์ƒํ™ฉ์— ๋งž๊ฒŒ ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ๋˜์–ด์•ผ ํ•จ
    • ์ผ๋ฐ˜์ ์ธ ๊ฒฝ์šฐ: "Authorization header required"
    • ์ฟ ํ‚ค ์ธ์ฆ์˜ ๊ฒฝ์šฐ: "Cookie authentication required"

๐Ÿ”จ ๋‘ ๋ฒˆ์งธ ์‹œ๋„: ๋ช…์„ธ ๊ธฐ๋ฐ˜ ์ˆ˜์ •

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

 

์˜ˆ๋ฅผ ๋“ค์–ด ํ•จ์ˆ˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋‚˜ ๋“ค์—ฌ์“ฐ๊ธฐ, ๊ด„ํ˜ธ ์œ„์น˜ ๋“ฑ์ด ํ”„๋กœ์ ํŠธ์˜ ๊ธฐ์กด ์Šคํƒ€์ผ๊ณผ ๋‹ฌ๋ž์ฃ .

// ์ œ๊ฐ€ ์ˆ˜์ •ํ•œ ์ฝ”๋“œ
function extractErrorsFromResults(
  results: (SecurityHandlerResult | SecurityHandlerResult[])[],
) {
  return results
    .map((result) => {
      if (Array.isArray(result)) {
        return result.map((it) => it).filter((it) => !it.success);
      }
      return [result].filter((it) => !it.success);
    })
    .flatMap((it) => [...it]);
}

// ์›๋ž˜ ํ”„๋กœ์ ํŠธ ์Šคํƒ€์ผ
function extractErrorsFromResults(results: (SecurityHandlerResult | SecurityHandlerResult[])[]) {
  return results.map(result => {
    if (Array.isArray(result)) {
      return result.map(it => it).filter(it => !it.success);
    }
    return [result].filter(it => !it.success);
  }).flatMap(it => [...it]);
}

 

์ด๋ฅผ ๋น ๋ฅด๊ฒŒ ์›๋ž˜ ํ”„๋กœ์ ํŠธ์˜ ์Šคํƒ€์ผ๋Œ€๋กœ ๋˜๋Œ๋ฆฌ๊ณ  ์ปค๋ฐ‹์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์„œ ๋์ด ์•„๋‹ˆ์—ˆ์ฃ . CI ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ๋Œ๋ ค๋ณด๋‹ˆ ๋‘ ๊ฐ€์ง€ ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ๊ฒฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค:

๐Ÿ› ํ…Œ์ŠคํŠธ ์‹คํŒจ ๋ฐœ๊ฒฌ

์ดํ›„ CI์—์„œ ๋‘ ๊ฐ€์ง€ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค:

  1. Basic ์ธ์ฆ์—์„œ ํ—ค๋”๊ฐ€ ์—†์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ
  2. undefined.includes() ํ˜ธ์ถœ๋กœ ์ธํ•œ ์—๋Ÿฌ
AssertionError: expected 'Cannot read properties of undefined (…' 
to equal 'Authorization header required'

 

๊ฒฐ๋ก ์€ authHeader๊ฐ€ undefined์ผ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๋ฌธ์ œ๋กœ ์ธํ•œ ํ…Œ์ŠคํŠธ ์‹คํŒจ์˜€์Šต๋‹ˆ๋‹ค.

โœจ ์ตœ์ข… ํ•ด๊ฒฐ์ฑ…

๋ชจ๋“  ํ”ผ๋“œ๋ฐฑ๊ณผ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ๊ณ ๋ คํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ตœ์ข… ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค:

private validateHttp(): void {
  if (['http'].includes(scheme.type.toLowerCase())) {
    const authHeader = req.headers['authorization']?.toLowerCase();
    const authCookie = req.cookies[scheme.name] || req.signedCookies?.[scheme.name];

    const type = scheme.scheme?.toLowerCase();
    if (type === 'bearer') {
      if (authHeader && !authHeader.includes('bearer')) {
        throw Error(`Authorization header with scheme 'Bearer' required`);
      }
      
      if (!authHeader && !authCookie) {
        throw Error(scheme.in === 'cookie' 
          ? `Cookie authentication required`
          : `Authorization header required`);
      }
    }

    if (type === 'basic') {
      if (!authHeader) {
        throw Error(`Authorization header required`);
      }
      if (!authHeader.includes('basic')) {
        throw Error(`Authorization header with scheme 'Basic' required`);
      }
    }
  }
}

์ฃผ์š” ๊ฐœ์„ ์‚ฌํ•ญ:

  1. Basic ์ธ์ฆ๊ณผ Bearer ์ธ์ฆ์˜ ๋ช…ํ™•ํ•œ ๊ตฌ๋ถ„
  2. ์ฟ ํ‚ค ๊ธฐ๋ฐ˜ ์ธ์ฆ ์ง€์› (OpenAPI ๋ช…์„ธ ์ค€์ˆ˜)
  3. ์ ์ ˆํ•œ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ
  4. undefined ์ฒดํฌ ๊ฐ•ํ™”

๐ŸŽ“ ๋ฐฐ์šด ์ 

  1. OpenAPI ๋ช…์„ธ๋ฅผ ๋” ๊ผผ๊ผผํžˆ ์‚ดํŽด๋ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค
  2. ํ”„๋กœ์ ํŠธ์˜ ์ „์ฒด ํ…Œ์ŠคํŠธ ์‹คํ–‰์˜ ์ค‘์š”์„ฑ์„ ๋‹ค์‹œ ํ•œ๋ฒˆ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค
  3. ์ฝ”๋“œ ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ๋ฅผ ์ค€์ˆ˜ํ•˜๋Š” ๊ฒƒ์˜ ์ค‘์š”์„ฑ์„ ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค

์ด๋ ‡๊ฒŒ ํ•ด์„œ express-openapi-validator์— ์ž‘์€ ๊ธฐ์—ฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์—ˆ๋„ค์š”. ์˜คํ”ˆ์†Œ์Šค ๊ธฐ์—ฌ๋Š” ์–ธ์ œ๋‚˜ ์ƒˆ๋กœ์šด ๋ฐฐ์›€์˜ ๊ธฐํšŒ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์—๋Š” ๋˜ ์–ด๋–ค ์žฌ๋ฏธ์žˆ๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ฒŒ ๋ ์ง€ ๊ธฐ๋Œ€๊ฐ€ ๋ฉ๋‹ˆ๋‹ค! ๐Ÿ˜Š

React ๊ณต์‹๋ฌธ์„œ!!

๋“ค์–ด๊ฐ€๋ฉฐ

์ตœ๊ทผ Allini ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๋ช‡ ๊ฐ€์ง€ ๊ณ ๋ฏผ์ด ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค.

over-fetching, waterfall ํ˜„์ƒ์œผ๋กœ ์ธํ•œ ์„ฑ๋Šฅ ์ €ํ•˜, ๊ทธ๋ฆฌ๊ณ  ๋ฌด์—‡๋ณด๋‹ค ๋น ๋ฅธ ์‹œ์žฅ ๊ฒ€์ฆ์„ ์œ„ํ•ด ๋‹ค์–‘ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ™œ์šฉํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ ์ฆ๊ฐ€ ๋ฌธ์ œ์˜€์ฃ .

 

์ข‹์€ ๊ฐœ๋ฐœ์ž๋Š” ๋‹จ์ˆœํžˆ ๋ชจ๋“  ๊ฒƒ์„ ์†์ˆ˜ ์ฒ˜์Œ๋ถ€ํ„ฐ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์ ์ ˆํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ ํƒํ•˜๊ณ  ์กฐ์žกํ•ด ๋น ๋ฅด๊ฒŒ ์ œํ’ˆ์„ ๋งŒ๋“ค์–ด๋‚ด๋Š” ๋Šฅ๋ ฅ์ด ์ค‘์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ์ด๋Ÿฌํ•œ ์ ‘๊ทผ์€ ํ•„์—ฐ์ ์œผ๋กœ ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ ์ฆ๊ฐ€๋ผ๋Š” ๋ฌธ์ œ๋ฅผ ๋™๋ฐ˜ํ•˜๊ฒŒ ๋˜์—ˆ๊ณ , ์ด๋Š” ์ œ๊ฐ€ React ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์— ๊ด€์‹ฌ์„ ๊ฐ–๊ฒŒ ๋œ ์ฃผ๋œ ๊ณ„๊ธฐ๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!

 

1. React ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์˜ ํ˜„์žฌ ์‚ฌ์šฉ ๊ฐ€๋Šฅ ์—ฌ๋ถ€

ํ˜„์žฌ ์ƒํƒœ

  • React 18 ๋ฒ„์ „: ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๋ฏธ์ง€์›
  • React 19 RC(Release Candidate): ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ์ง€์›
 

ํ•˜์ง€๋งŒ ํ˜„์žฌ React 18.2.0์„ ์‚ฌ์šฉ์ค‘์ธ ํ”„๋กœ์ ํŠธ์—์„œ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๊ธฐ๋Š” ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

React 19๋กœ์˜ ์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ๊ณ ๋ คํ•ด๋ณผ ์ˆ˜ ์žˆ์ง€๋งŒ, RC ๋ฒ„์ „์ด๊ธฐ ๋•Œ๋ฌธ์— ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ์˜ ์‚ฌ์šฉ์€ ์‹ ์ค‘ํžˆ ๊ฒฐ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

2. SSR๊ณผ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์˜ ๊ด€๊ณ„

ํ•™์Šต ์ดˆ๊ธฐ์— ๊ฐ€์žฅ ํ—ท๊ฐˆ๋ ธ๋˜ ๊ฐœ๋…์ž…๋‹ˆ๋‹ค!

SSR (Server-Side Rendering)

  • ๋ชฉ์ : ์ดˆ๊ธฐ ํŽ˜์ด์ง€ ๋กœ๋“œ ์„ฑ๋Šฅ ๊ฐœ์„  ๋ฐ SEO ์ตœ์ ํ™”
  • ๋™์ž‘ ๋ฐฉ์‹:
    • ์„œ๋ฒ„์—์„œ ์ „์ฒด ํŽ˜์ด์ง€์˜ ์ดˆ๊ธฐ HTML์„ ์ƒ์„ฑ
    • ํด๋ผ์ด์–ธํŠธ๋กœ HTML๊ณผ JS ๋ฒˆ๋“ค์„ ์ „์†ก
    • ํด๋ผ์ด์–ธํŠธ์—์„œ hydration ๊ณผ์ •์„ ํ†ตํ•ด ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒํ•œ ์•ฑ์œผ๋กœ ์ „ํ™˜

์ด์ „ React ๊ณต์‹ ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด, React์˜ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง ๋ฉ”์„œ๋“œ๋“ค์€ ํฌ๊ฒŒ ์„ธ ๊ฐ€์ง€ ํ™˜๊ฒฝ์œผ๋กœ ๊ตฌ๋ถ„๋ฉ๋‹ˆ๋‹ค:

 

Node.js Streams ํ™˜๊ฒฝ์šฉ:

  • renderToPipeableStream()
  • renderToNodeStream() (Deprecated)
  • renderToStaticNodeStream()

Web Streams ํ™˜๊ฒฝ์šฉ (๋ธŒ๋ผ์šฐ์ €, Deno, modern edge runtimes):

  • renderToReadableStream()

์ŠคํŠธ๋ฆผ์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ํ™˜๊ฒฝ์šฉ:

  • renderToString()
  • renderToStaticMarkup()

๊ธฐ์กด SSR์˜ ๋™์ž‘ ๋ฐฉ์‹์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

// ๊ธฐ์กด SSR์˜ ๋™์ž‘ ๋ฐฉ์‹
// 1. ์„œ๋ฒ„์—์„œ ์ดˆ๊ธฐ ๋ Œ๋”๋ง
const app = ReactDOMServer.renderToPipeableStream(<App />);
// ํ•œ๋ฒˆ์— ์ „์ฒด HTML์„ ์ƒ์„ฑํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ๋กœ ์ „์†ก

// 2. HTML ๋ฌธ์„œ์— ์‚ฝ์ž…
const html = `
  <!doctype html>
  <html>
    <body>
      <div id="root">${app}</div>
      <script src="/bundle.js"></script>
    </body>
  </html>
`;

 

 

๊ณต์‹ ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด, renderToPipeableStream์„ ํ†ตํ•ด ์ดˆ๊ธฐ ์…ธ(shell)์„ ๋ Œ๋”๋งํ•˜๊ณ  ์ „์†กํ•ฉ๋‹ˆ๋‹ค.

renderToPipeableStream์˜ ๊ธฐ๋ณธ ๊ฐœ๋…์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • Node.js ํ™˜๊ฒฝ์—์„œ React ํŠธ๋ฆฌ๋ฅผ HTML ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” API์ž…๋‹ˆ๋‹ค.
  • ์ ์ง„์ ์ธ ๋กœ๋”ฉ์ด ๊ฐ€๋Šฅํ•œ ์ŠคํŠธ๋ฆฌ๋ฐ SSR์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์‹œ ์ฝ”๋“œ

// ๋ฐœ์ „๋œ SSR (์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐฉ์‹)
const { pipe } = renderToPipeableStream(<App />, {
  bootstrapScripts: ['/main.js'],
  onShellReady() {
    // 1. ๋จผ์ € ๊ธฐ๋ณธ ๋ผˆ๋Œ€(shell)๋ฅผ ๋ณด๋ƒ„
    // ์ดˆ๊ธฐ shell(Suspense ๊ฒฝ๊ณ„ ์œ„์˜ ์ฝ˜ํ…์ธ )์ด ์ค€๋น„๋˜๋ฉด ์ŠคํŠธ๋ฆฌ๋ฐ ์‹œ์ž‘

    pipe(response);
  },
  onAllReady() {
    // ๋ชจ๋“  ์ฝ˜ํ…์ธ ๊ฐ€ ์ค€๋น„๋˜๋ฉด ํ˜ธ์ถœ (ํฌ๋กค๋Ÿฌ๋‚˜ ์ •์  ์ƒ์„ฑ์šฉ)
  }
});

 

๊ธฐ์กด์˜ renderToString์€ ์ œํ•œ์ ์ธ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋ฐ˜๋ฉด, renderToPipeableStream์€ Suspense๋ฅผ ์™„์ „ํžˆ ์ง€์›ํ•˜๊ณ  HTML ์ŠคํŠธ๋ฆฌ๋ฐ์ด ๊ฐ€๋Šฅํ•œ ๋” ๋ฐœ์ „๋œ ํ˜•ํƒœ์˜ SSR ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

 

์ฃผ์š” ์ฐจ์ด์ :

  • ๊ธฐ์กด SSR: ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค ๋ชจ์•„์„œ ํ•œ๋ฒˆ์— ๋ณด๋ƒ„ (๋А๋ฆผ)
  • ์ƒˆ๋กœ์šด SSR:
    1. ๋จผ์ € ํŽ˜์ด์ง€์˜ ๊ธฐ๋ณธ ๋ผˆ๋Œ€๋ฅผ ๋ณด๋‚ด๊ณ 
    2. ๋‚˜๋จธ์ง€ ๋‚ด์šฉ์€ ์ค€๋น„๋˜๋Š” ๋Œ€๋กœ ์กฐ๊ธˆ์”ฉ ๋ณด๋ƒ„ (๋” ๋น ๋ฆ„)

 

์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด, ๊ธฐ์กด SSR์€ "์‹๋‹น์—์„œ ๋ชจ๋“  ์š”๋ฆฌ๊ฐ€ ์™„์„ฑ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ํ•œ๋ฒˆ์— ์„œ๋น™"ํ•˜๋Š” ๊ฒƒ์ด๊ณ , ์ƒˆ๋กœ์šด ๋ฐฉ์‹์€ "์ค€๋น„๋œ ๋ฉ”๋‰ด๋ถ€ํ„ฐ ๋จผ์ € ์„œ๋น™ํ•˜๊ณ , ๋‚˜๋จธ์ง€๋Š” ์™„์„ฑ๋˜๋Š” ๋Œ€๋กœ ๊ฐ€์ ธ๋‹ค์ฃผ๋Š”" ๊ฒƒ๊ณผ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค.

 

React ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ (RSC)

  • ๋ชฉ์ : ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ ์ตœ์ ํ™” ๋ฐ ์„œ๋ฒ„ ๋ฆฌ์†Œ์Šค ์ง์ ‘ ์ ‘๊ทผ
  • ๋™์ž‘ ๋ฐฉ์‹:
    • ํŠน์ • ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„œ๋ฒ„์—์„œ๋งŒ ์‹คํ–‰๋˜๋„๋ก ์ง€์ •
    • ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋ง๋œ ๊ฒฐ๊ณผ๋งŒ ํด๋ผ์ด์–ธํŠธ๋กœ ์ „์†ก
    • JS ๋ฒˆ๋“ค์— ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ์ฝ”๋“œ๋Š” ๋ฏธํฌํ•จ

๋‘ ๊ธฐ์ˆ ์˜ ์ƒํ˜ธ๋ณด์™„์  ๊ด€๊ณ„

SSR๊ณผ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” ์„œ๋กœ๋ฅผ ๋Œ€์ฒดํ•˜๋Š” ๊ธฐ์ˆ ์ด ์•„๋‹Œ, ๋ณด์™„ํ•˜๋Š” ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค:

  1. ์—ญํ•  ๋ถ„๋‹ด
    • SSR: ์ดˆ๊ธฐ ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ ์ „์ฒด HTML ์ƒ์„ฑ
    • ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ: ํŠน์ • ์ปดํฌ๋„ŒํŠธ๋“ค์˜ ์„œ๋ฒ„ ์‹คํ–‰ ๋ฐ ๋ฒˆ๋“ค ํฌ๊ธฐ ์ตœ์ ํ™”

 

3. ํ”„๋ ˆ์ž„์›Œํฌ๋“ค์˜ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๊ตฌํ˜„

Next.js์˜ ์ ‘๊ทผ

Next.js๋Š” ์ž์ฒด์ ์ธ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๊ตฌํ˜„์„ ํ†ตํ•ด React์˜ ์‹คํ—˜์  ๊ธฐ๋Šฅ์„ ์•ˆ์ •์ ์œผ๋กœ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

  1. React ์ฝ”์–ด ํŒ€๊ณผ Next.js ํŒ€์˜ ํ˜‘๋ ฅ
    • React ์ฝ”์–ด ํŒ€์˜ ์ฃผ์š” ๋ฉค๋ฒ„๋“ค(Joe Savona, Sebastian Markbåge ๋“ฑ)์ด Vercel(Next.js)์— ํ•ฉ๋ฅ˜
    • ์ด๋ฅผ ํ†ตํ•ด React์˜ ์‹คํ—˜์  ๊ธฐ๋Šฅ์„ Next.js์—์„œ ๋จผ์ € ์•ˆ์ •์ ์œผ๋กœ ๊ตฌํ˜„ ๊ฐ€๋Šฅ
  2. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์˜ ๋ฐœ์ „ ๊ณผ์ •
    • 2020๋…„: React ํŒ€์ด ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๊ฐœ๋… ์ฒซ ๋ฐœํ‘œ
    • React 18: ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๊ธฐ์ดˆ ์ธํ”„๋ผ ํฌํ•จ, but ์™„์ „ํ•œ ๊ตฌํ˜„์€ ์•„๋‹˜
    • Next.js 13: App Router์™€ ํ•จ๊ป˜ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ์ „๋ฉด ๋„์ž…

๊ธฐ์ˆ ์  ๊ตฌํ˜„ ๋ฐฉ์‹

React์˜ Server Components์— ๋Œ€ํ•œ ๊ณต์‹๋ฌธ์„œ๋ฅผ ๋ณด๋ฉด,

๋ฌธ์„œ์—์„œ๋Š” Server Components ๊ตฌํ˜„์— ๋Œ€ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์–ธ๊ธ‰ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค:

"To support React Server Components as a bundler or framework, we recommend pinning to a specific React version, or using the Canary release. We will continue working with bundlers and frameworks to stabilize the APIs used to implement React Server Components in the future."

์ด๋Š” Server Components ๊ตฌํ˜„์„ ์œ„ํ•œ ๋ฒˆ๋“ค๋Ÿฌ API๊ฐ€ ์•„์ง ์™„์ „ํžˆ ์•ˆ์ •ํ™”๋˜์ง€ ์•Š์•˜์Œ์„ ์‹œ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

 

Next.js๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค:

// next.config.js
{
  webpack: (config, { isServer }) => {
    if (isServer) {
      // ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ์ฒ˜๋ฆฌ
      config.plugins.push(new webpack.DefinePlugin({
        // marked, sanitize-html ๊ฐ™์€ ์„œ๋ฒ„ ์ „์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด
        // ํด๋ผ์ด์–ธํŠธ ๋ฒˆ๋“ค์— ํฌํ•จ๋˜์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌ
        'process.env.SERVER_ONLY': JSON.stringify(true)
      }));
      
      config.module.rules.push({
        test: /\.(js|jsx|ts|tsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-react'],
            plugins: [
              // async ์ปดํฌ๋„ŒํŠธ ์ง€์›์„ ์œ„ํ•œ ๋ณ€ํ™˜
              '@babel/plugin-syntax-top-level-await',
              // Server Component์˜ import ๊ตฌ๋ฌธ ์ฒ˜๋ฆฌ
              ['@babel/plugin-transform-modules-commonjs', {
                importInterop: 'node'
              }]
            ]
          }
        }
      });
    } else {
      // ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ์ฒ˜๋ฆฌ
      config.module.rules.push({
        test: /\.(js|jsx|ts|tsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-react'],
            plugins: [
              // "use client" ์ง€์‹œ์–ด๊ฐ€ ์žˆ๋Š” ํŒŒ์ผ๋งŒ ํด๋ผ์ด์–ธํŠธ ๋ฒˆ๋“ค์— ํฌํ•จ
              ['./plugins/client-directive', {}],
              // Server Component์˜ ์ถœ๋ ฅ์„ ํด๋ผ์ด์–ธํŠธ์—์„œ hydrateํ•˜๊ธฐ ์œ„ํ•œ ๋ณ€ํ™˜
              ['./plugins/server-reference', {}]
            ]
          }
        }
      });
    }
    return config;
  }
}

// Server Component ๋ Œ๋”๋ง ํ•จ์ˆ˜
async function renderServerComponent(Component, props) {
  // ๋ฌธ์„œ์— ์–ธ๊ธ‰๋œ ๊ฒƒ์ฒ˜๋Ÿผ build time ๋˜๋Š” request time์— ์‹คํ–‰ ๊ฐ€๋Šฅ
  const stream = await ReactServerDOM.renderToReadableStream(
    <Component {...props} />,
    // ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์˜ ๋ฒˆ๋“ค ์ •๋ณด๋ฅผ ๋‹ด์€ ๋งคํ•‘
    webpackMap
  );
  
  // React์˜ Flight ํฌ๋งท์œผ๋กœ ์ง๋ ฌํ™”
  return encodeRSCPayload(stream);
}

 

์ด ๊ตฌํ˜„์€ ๊ณต์‹ ๋ฌธ์„œ์˜ ๋‹ค์Œ ๊ฐœ๋…๋“ค์„ ๋ฐ˜์˜ํ•ฉ๋‹ˆ๋‹ค:

  1. "Server Components are a new type of Component that renders ahead of time, before bundling, in an environment separate from your client app or SSR server."
    • webpack ์„ค์ •์—์„œ isServer ํ”Œ๋ž˜๊ทธ๋กœ ํ™˜๊ฒฝ์„ ๋ถ„๋ฆฌ
  2. "Server Components can run once at build time on your CI server, or they can be run for each request using a web server."
    • renderServerComponent ํ•จ์ˆ˜๊ฐ€ ์ด ๋‘ ๊ฐ€์ง€ ์ผ€์ด์Šค๋ฅผ ๋ชจ๋‘ ์ง€์›
  3. "Async Components are a new feature of Server Components that allow you to await in render."
    • ์„œ๋ฒ„ ์ธก babel ์„ค์ •์— @babel/plugin-syntax-top-level-await ํฌํ•จ

ํ•ต์‹ฌ ๊ตฌํ˜„ ์š”์†Œ

  1. RSC ํฌ๋งท
    • ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์˜ ๋ Œ๋”๋ง ๊ฒฐ๊ณผ๋ฅผ ํŠน๋ณ„ํ•œ ํฌ๋งท์œผ๋กœ ์ง๋ ฌํ™”
    • JSON์ด ์•„๋‹Œ ํŠน๋ณ„ํ•œ ๋ฐ”์ด๋„ˆ๋ฆฌ ํ˜•์‹ ์‚ฌ์šฉ
    • React Flight ์•„ํ‚คํ…์ฒ˜ ํ™œ์šฉ
  2. ๋ฒˆ๋“ค๋ง ์ตœ์ ํ™”
    • ์„œ๋ฒ„ ์ „์šฉ ์ฝ”๋“œ๋ฅผ ํด๋ผ์ด์–ธํŠธ ๋ฒˆ๋“ค์—์„œ ์ œ์™ธ
    • ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋งŒ ์„ ํƒ์ ์œผ๋กœ ๋ฒˆ๋“ค๋ง
  3. ์ŠคํŠธ๋ฆฌ๋ฐ ์ง€์›
    • React Suspense์™€ ํ†ตํ•ฉ
    • ์ ์ง„์ ์ธ ํŽ˜์ด์ง€ ๋กœ๋”ฉ ๊ตฌํ˜„

์ดํ•ด๋ฅผ ์œ„ํ•ด ๊ธฐ๋ณธ์ ์ธ ํ‹€์— ๋งž์ถฐ ๊ฐ„์†Œํ™”ํ•ด ๊ตฌํ˜„ํ•ด ๋ดค์Šต๋‹ˆ๋‹ค!
ํ˜น์‹œ ํ‹€๋ฆฐ ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ํŽธํ•˜๊ฒŒ ๋Œ“๊ธ€ ์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!

Remix์˜ ์ ‘๊ทผ

Remix๋Š” ๋ผ์šฐํ„ฐ ๊ธฐ๋ฐ˜์˜ ์„œ๋ฒ„ ์ค‘์‹ฌ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ฑ„ํƒํ•˜์—ฌ ์œ ์‚ฌํ•œ ์ด์ ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

// Remix์˜ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋กœ์ง ์˜ˆ์‹œ
export async function loader({ request }) {
  const data = await getProducts();
  return json(data);
}

export default function Products() {
  const products = useLoaderData();
  return (
    <div>
      <h1>Products</h1>
      <ProductList products={products} />
    </div>
  );
}

 

4. Next.js ์•ฑ ๋ผ์šฐํ„ฐ์™€ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์˜ ๊ด€๊ณ„

Next.js์˜ ์•ฑ ๋ผ์šฐํ„ฐ๋Š” ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ฑ„ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.  ๋งค์šฐ ์ค‘์š”ํ•œ ์•„ํ‚คํ…์ฒ˜์  ๊ฒฐ์ •์ด์ฃ ?!

๊ธฐ๋ณธ ๊ตฌ์กฐ

// app/layout.jsx (์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ)
export default function RootLayout({ children }) {
  return (
    <html>
      <body>{children}</body>
    </html>
  );
}

// app/page.jsx (์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ)
async function Home() {
  const data = await getData(); // ์„œ๋ฒ„์—์„œ ์ง์ ‘ ๋ฐ์ดํ„ฐ fetch
  
  return (
    <main>
      <h1>Welcome</h1>
      <ClientComponent data={data} />
    </main>
  );
}

// components/client-component.jsx
'use client';

function ClientComponent({ data }) {
  // ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ์ธํ„ฐ๋ž™์…˜์ด ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ
  return <div>{/* ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ UI */}</div>;
}

 

์ฃผ์š” ํŠน์ง•

์ž๋™ ์ฝ”๋“œ ๋ถ„ํ• 

// app/products/page.jsx
import { ProductList } from './components/product-list';

async function ProductsPage() {
  const products = await fetchProducts(); // ์„œ๋ฒ„์—์„œ๋งŒ ์‹คํ–‰
  return <ProductList products={products} />;
}

// ์ด ์ปดํฌ๋„ŒํŠธ์™€ ๊ด€๋ จ๋œ ์„œ๋ฒ„ ๋กœ์ง์€ ํด๋ผ์ด์–ธํŠธ ๋ฒˆ๋“ค์— ํฌํ•จ๋˜์ง€ ์•Š์Œ

 

 

์ŠคํŠธ๋ฆฌ๋ฐ๊ณผ Suspense ํ†ตํ•ฉ

// app/dashboard/page.jsx
import { Suspense } from 'react';

export default function Dashboard() {
  return (
    <div>
      <Suspense fallback={<LoadingUI />}>
        <SlowComponent />
      </Suspense>
    </div>
  );
}
 

๋งˆ์น˜๋ฉฐ

ํ˜„์žฌ ์ œ๊ฐ€ ์ง„ํ–‰ ์ค‘์ธ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” React 18.2.0์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๊ธฐ๋Š” ์–ด๋ ค์šด ์ƒํ™ฉ์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๊ณ ๊ฐ ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ›์€ ํ›„ Next.js๋กœ์˜ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ๊ณ ๋ คํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์˜ ์ด์ ์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ๊ธฐํšŒ๊ฐ€ ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

ํ”„๋กœ์ ํŠธ๊ฐ€ ์ปค์ง€๋ฉฐ, ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ ์ตœ์ ํ™”์™€ ์„œ๋ฒ„ ๋ฆฌ์†Œ์Šค ํ™œ์šฉ ์ธก๋ฉด์—์„œ ํฐ ์ด์ ์„ ์ œ๊ณตํ•  ๊ฒƒ์œผ๋กœ ๊ธฐ๋Œ€๋ฉ๋‹ˆ๋‹ค!

 

 

 

๐Ÿ“ƒ ์ฐธ๊ณ  ๋ฌธํ—Œ  
React 19 RC!!!
React RFC ๊นƒํ—™
Next.js App Router RFC ๊นƒํ—™
Next.js Server Components ๊ณต์‹๋ฌธ์„œ
React CoreํŒ€ Dan Abramov์˜ Server Components ์†Œ๊ฐœ ๋ฐœํ‘œ
์นด์นด์˜คํŽ˜์ด - React Server Components
์š”์ฆ˜ IT - React Server Components
freeCodeCamp - React Server Components

 

๋ฌดํ•œ ์Šคํฌ๋กค๊ณผ ๋ฒ„์ธ„์–ผ ์Šคํฌ๋กค์„ ๋„์ž… ์ค‘์ธ ํŽ˜์ด์ง€์ž…๋‹ˆ๋‹ค!

 

ํ”„๋กœ์ ํŠธ์— ๋„์ž…๋  ๋ฌดํ•œ ์Šคํฌ๋กค์„ ๊ฐœ๋ฐœ ์ค‘์ธ ์ƒํ™ฉ์ž…๋‹ˆ๋‹ค.
๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๋˜ ์ค‘ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฌด์ˆ˜ํžˆ ๋งŽ์•„์งˆ ๊ฒฝ์šฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง„ ์•Š์„๊นŒ๋ผ๋Š” ์ƒ๊ฐ์— ๋ฌดํ•œ ์Šคํฌ๋กค์— ๋Œ€ํ•œ ์ตœ์ ํ™”๋ฅผ ๊ณ ๋ฏผํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!
์—ฌ๋Ÿฌ ์ตœ์ ํ™” ์ค‘ ํ•˜๋‚˜๋Š” ๋ฒ„์ธ„์–ผ ์Šคํฌ๋กค์— ๋Œ€ํ•ด ํŒŒํŠธ๋ฅผ ๋‚˜๋ˆ  ๊ธฐ๋กํ•ด๋‘๋ ค ํ•ด์š”!

 

๋ฒ„์ธ„์–ผ ์Šคํฌ๋กค ์ด๋ž€?

๋ฒ„์ธ„์–ผ ์Šคํฌ๋กค(Virtual Scroll)์€ ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ์ˆ ๋กœ, ์‚ฌ์šฉ์ž๊ฐ€ ์‹ค์ œ๋กœ ๋ณด๊ณ  ์žˆ๋Š” ํ•ญ๋ชฉ๋งŒ ๋ Œ๋”๋งํ•˜์—ฌ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค.

 

ํŠนํžˆ, ๋ฆฌ์ŠคํŠธ๋‚˜ ๊ทธ๋ฆฌ๋“œ ํ˜•์‹์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

์ด๋ฒˆ ๋ธ”๋กœ๊ทธ์—์„œ๋Š” ๋ฒ„์ธ„์–ผ ์Šคํฌ๋กค์„ ์‚ฌ์šฉํ•ด์•ผ ํ•  ์ƒํ™ฉ๊ณผ, ์‹ค์ œ ์ƒ์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ์˜ ๊ตฌํ˜„ ๋ฐฉ์‹์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์ƒ์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•œ ์„ค๋ช…์€ ๋ณ„๋„์˜ ๊ธ€๋กœ ๋ถ„๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค!

 

๋ฒ„์ธ„์–ผ ์Šคํฌ๋กค์„ ์–ด๋А ์ƒํ™ฉ์—์„œ ์จ์•ผ ํ• ๊นŒ?

๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ

๋ฒ„์ธ„์–ผ ์Šคํฌ๋กค์€ ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ํ•„์ˆ˜์ ์ด๋ผ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž์—๊ฒŒ ์ˆ˜์ฒœ ์ˆ˜๋งŒ ๊ฐœ์˜ ํ•ญ๋ชฉ์ด ํฌํ•จ๋œ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ณด์—ฌ์ค˜์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ, ๋ชจ๋“  ํ•ญ๋ชฉ์„ ํ•œ ๋ฒˆ์— ๋ Œ๋”๋งํ•˜๋ฉด ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ด๋Ÿด ๋•Œ, ํ˜„์žฌ ํ™”๋ฉด์— ๋ณด์ด๋Š” ํ•ญ๋ชฉ๋งŒ ๋ Œ๋”๋งํ•จ์œผ๋กœ์จ ๋ Œ๋”๋ง ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์Šคํฌ๋กค ์„ฑ๋Šฅ ๊ฐœ์„ 

์ผ๋ฐ˜์ ์ธ ์Šคํฌ๋กค ๋ฐฉ์‹์—์„œ๋Š” ๋ชจ๋“  ๋ฆฌ์ŠคํŠธ ํ•ญ๋ชฉ์ด DOM์— ์กด์žฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ์Šคํฌ๋กคํ•  ๋•Œ ํ™”๋ฉด์— ๋ณด์ด๋Š” ํ•ญ๋ชฉ์€ ๋ฐ”๋€Œ์ง€๋งŒ, DOM์—์„œ ๋ชจ๋“  ์š”์†Œ๊ฐ€ ๊ณ„์† ์œ ์ง€๋˜๋ฏ€๋กœ ์ง€์›Œ์ง€๊ฑฐ๋‚˜ ์ถ”๊ฐ€๋˜๋Š” ๊ณผ์ •์ด ์—†์Šต๋‹ˆ๋‹ค.

๋Œ€์‹ , ๋ธŒ๋ผ์šฐ์ €๋Š” ์Šคํฌ๋กค์— ๋”ฐ๋ผ ๋ณด์ด์ง€ ์•Š๋Š” ํ•ญ๋ชฉ๋“ค์„ ์—ฌ์ „ํžˆ ๋ฉ”๋ชจ๋ฆฌ์— ๋‘๊ณ  ์žˆ์–ด, ์ „์ฒด ๋ฆฌ์ŠคํŠธ์˜ ํฌ๊ธฐ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก ๋ Œ๋”๋ง ์„ฑ๋Šฅ์ด ์ €ํ•˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋ฐ˜๋ฉด, ๊ฐ€์ƒ ์Šคํฌ๋กค์—์„œ๋Š” ์Šคํฌ๋กคํ•  ๋•Œ ํ˜„์žฌ ํ™”๋ฉด์— ๋ณด์ด๋Š” ํ•ญ๋ชฉ๋งŒ DOM์— ๋ Œ๋”๋งํ•˜๊ณ , ๋‚˜๋จธ์ง€ ํ•ญ๋ชฉ์€ DOM์—์„œ ์ง€์šฐ๊ฑฐ๋‚˜ ์ˆจ๊น๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•จ์œผ๋กœ์จ, ๋ฉ”๋ชจ๋ฆฌ์— ์œ ์ง€๋˜๋Š” ์š”์†Œ์˜ ์ˆ˜๊ฐ€ ์ค„์–ด๋“ค์–ด ์„ฑ๋Šฅ์ด ๊ฐœ์„ ๋ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ์Šคํฌ๋กคํ•  ๋•Œ ํ•„์š”ํ•œ ๋งŒํผ์˜ ํ•ญ๋ชฉ๋งŒ ์ƒ์„ฑํ•˜๊ณ , ๋ณด์ด์ง€ ์•Š๋Š” ํ•ญ๋ชฉ์€ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œํ•˜๊ฒŒ ๋˜์–ด, ๋ Œ๋”๋ง ๊ณผ์ •์—์„œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ์š”์†Œ์˜ ์ˆ˜๊ฐ€ ์ ์–ด์ง€๋ฏ€๋กœ ์Šคํฌ๋กค ์„ฑ๋Šฅ์ด ๋”์šฑ ์›ํ™œํ•ด์ง€๊ฒ ์ฃ ?!

 

๊ฒฐ๋ก ์ ์œผ๋กœ, ์ผ๋ฐ˜ ์Šคํฌ๋กค์—์„œ๋Š” DOM์˜ ํ•ญ๋ชฉ์ด ์ง€์›Œ์ง€์ง€ ์•Š์ง€๋งŒ, ๊ฐ€์ƒ ์Šคํฌ๋กค์€ ํ•„์š” ์—†๋Š” ํ•ญ๋ชฉ์„ ์ œ๊ฑฐํ•˜์—ฌ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ํ–ฅ์ƒ

ํŽ˜์ด์ง€ ๋‚ด์—์„œ์˜ ์›ํ™œํ•œ ์Šคํฌ๋กค๋ง ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด์„œ๋„ ๋ฒ„์ธ„์–ผ ์Šคํฌ๋กค์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

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

 

๋ฆฌ๋ Œ๋”๋ง ์„ฑ๋Šฅ ๋˜ํ•œ ๊ณ ๋ คํ•ด์•ผ ํ•  ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.

์ผ๋ฐ˜์ ์ธ ์Šคํฌ๋กค ๋ฐฉ์‹์—์„œ๋Š” ๋ชจ๋“  ๋ฆฌ์ŠคํŠธ ํ•ญ๋ชฉ์ด DOM์— ์กด์žฌํ•˜๋ฏ€๋กœ, ์‚ฌ์šฉ์ž๊ฐ€ ์Šคํฌ๋กคํ•  ๋•Œ๋งˆ๋‹ค ๊ธฐ์กด ์š”์†Œ๊ฐ€ ์žฌ๋ Œ๋”๋ง๋˜๊ฑฐ๋‚˜ ์œ„์น˜๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

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

 

๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋„ ํ™”๋ฉด์— ๋ณด์ด๋Š” ํ•ญ๋ชฉ๋งŒ์„ ํšจ์œจ์ ์œผ๋กœ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์–ด, ์ „์ฒด์ ์ธ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.

 

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

 

์‹ค์ œ๋กœ ๋ฒ„์ธ„์–ผ ์Šคํฌ๋กค์„ ์ƒ์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๋งŒ๋“  ๊ณณ์—์„  ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ–ˆ์„๊นŒ?

์ƒ์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ตฌํ˜„ ๋ฐฉ์‹

 react-window, react-virtualized, react-virtuoso ๊ด€๋ จ๋œ ๋ณ„๋„์˜ ๊ธ€๋กœ ๋ถ„๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค!

 

๋งˆ๋ฌด๋ฆฌ

๋ฒ„์ธ„์–ผ ์Šคํฌ๋กค์€ ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ , ์‚ฌ์šฉ์ž์—๊ฒŒ ์›ํ™œํ•œ ์Šคํฌ๋กค๋ง ๊ฒฝํ—˜์„ ์ œ๊ณตํ• ๋•Œ ๋งค๋ ฅ์žˆ๋Š” ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค!

๋‹ค์–‘ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด ๊ฐ๊ธฐ ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ํ”„๋กœ์ ํŠธ์˜ ์š”๊ตฌ์‚ฌํ•ญ์— ๋”ฐ๋ผ ์ ํ•ฉํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

 

์ €๋Š” ํ•™์Šต ๋ฐ ๊ฐœ์„ ์ ์„ ์ฐพ๊ธฐ์œ„ํ•ด ์Šค์Šค๋กœ ๊ตฌํ˜„์ค‘์— ์žˆ์Šต๋‹ˆ๋‹ค!

 

๋ฒ„์ธ„์–ผ ์Šคํฌ๋กค์ด ํ•„์š”ํ•œ ์ด์œ ์— ๋Œ€ํ•œ ๊ฒฝํ—˜

์‹ค์ œ๋กœ ํ˜„์žฌ ํšŒ์‚ฌ์—์„œ ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๊ฒŒ ๋  ๊ฒฝ์šฐ, DOM์˜ ํฌ๊ธฐ๊ฐ€ ์ปค์ง์— ๋”ฐ๋ผ ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์—ˆ์–ด์š”.

 

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

์ด๋Ÿฌํ•œ ์ด์œ ๋กœ ๋ฒ„์ธ„์–ผ ์Šคํฌ๋กค์„ ๋„์ž…ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋‹น์žฅ์€ ๊ตฌํ˜„๋œ ํ”„๋กœ์ ํŠธ์— ๋ฌธ์ œ๊ฐ€ ์—†์–ด๋ณด์—ฌ๋„ ๋ฐ์ดํ„ฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ , ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•œ ์„ ์ œ์ ์ธ ์กฐ์น˜๋กœ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

 

๋ฒ„์ธ„์–ผ ์Šคํฌ๋กค์„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ ๊ธฐ๋ฒ•๋“ค์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์Šคํฌ๋กค์„ ํ†ตํ•ด ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•  ๋•Œ, ๋‚จ์€ ๊ฐ€์ƒ ์Šคํฌ๋กค ์•„์ดํ…œ์ด ์žˆ๋Š”์ง€ ํŒ๋‹จํ•˜๋Š” ๋กœ์ง์„ ํ†ตํ•ด ํ•„์š”ํ•œ ๋งŒํผ๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๋„๋ก ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ณผ์ •์—์„œ ๋ฐ์ดํ„ฐ์˜ ์–‘๊ณผ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ณต์žก์„ฑ์„ ๊ณ ๋ คํ•˜์—ฌ ์ตœ์ ์˜ ์„ฑ๋Šฅ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์†์ ์ธ ๋…ธ๋ ฅ์ค‘์ž…๋‹ˆ๋‹ค!

 

 

 

๐Ÿ“ƒ ์ฐธ๊ณ  ๋ฌธํ—Œ  
์˜ค๋Š˜์˜์ง‘ ๋‚ด ๋ฌดํ•œ ์Šคํฌ๋กค ๊ฐœ๋ฐœ๊ธฐ
React Window ๊นƒํ—™
React Virtualized ๊นƒํ—™
React Virtuoso ๊นƒํ—™

 

์›ํ‹ฐ๋“œ ํ”„๋ฆฌ์˜จ๋ณด๋”ฉ ๊ณผ์ œ๋กœ ์ง„ํ–‰ํ–ˆ๋˜ ๋ฌดํ•œ์Šคํฌ๋กค์— ์ ์šฉํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

 

ํ•ด๋‹น ๊ธ€์—์„  ๋ฒ„์ถ”์–ผ ์Šคํฌ๋กค์„ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๊ธฐ ์ „์— ๋„์ž…์„ ์œ„ํ•ด ์ฐพ์•„๋ณธ ์ด๋ฏธ ์ƒ์šฉํ™”๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•ด ์ž‘์„ฑํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!

 

์ธ๊ธฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐœ์š”

๊ฒ€ํ† ํ•ด๋ณธ ๋ฒ„์ธ„์–ผ ์Šคํฌ๋กค์„ ์ง€์›ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ๋Š” react-window, react-virtualized, react-virtuoso๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๊ณ ์œ ํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์„ ํ†ตํ•ด ๋ฒ„์ธ„์–ผ ์Šคํฌ๋กค์„ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ๋‹ค์–‘ํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€์™€ ์„ฑ๋Šฅ ์š”๊ตฌ์‚ฌํ•ญ์— ๋งž์ถ”์–ด ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

์ด ์ค‘์—์„œ React Virtualized๋Š” ๊ฐ€๋ฒผ์›Œ์ง„ ๋ฒ„์ „์ธ React Window๊ฐ€ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋ฉฐ, React Virtuoso๋Š” ๋น„๊ต์  ์ตœ๊ทผ์— ์ถœ์‹œ๋˜์–ด ์‚ฌ์šฉ๋Ÿ‰์ด ์ ์ง€๋งŒ ์ ์  ์ธ๊ธฐ๋ฅผ ๋Œ๊ณ  ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. React Virtuoso๋Š” React Window์—๋Š” ์—†๋Š” ๊ธฐ๋Šฅ๋“ค์„ ์ง€์›ํ•˜์—ฌ ๊ฐ€๋ณ๊ณ  ๊ฐ„๋‹จํ•˜๋ฉด์„œ๋„ ๋‹ค์–‘ํ•œ ์š”๊ตฌ์— ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋“ค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๊ฐ€์ƒ ์Šคํฌ๋กค์„ ํ†ตํ•ด ์Šคํฌ๋กค์„ ๋‚ด๋ฆด ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ๋Š๊น€ ์—†์ด ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋Š” windowing ์ฒ˜๋ฆฌ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

 

Windowing

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

 

์—˜๋ฆฌ๋‹˜์˜ ๋“œ๋ฆผ ์ฝ”๋”ฉ Express ๊ฐ•์˜์˜ Rate Limit ๊ด€๋ จ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ํ•™์Šตํ•˜๋ฉฐ ์•Œ๊ฒŒ ๋๋˜ ๊ฐœ๋…์ธ๋ฐ ๋‹ค์‹œ ๋ณด๋‹ˆ ๊ดœํžˆ ๋ฐ˜๊ฐ‘๋„ค์š”! ใ…Žใ…Ž

 

์Šคํฌ๋กค ์œ„์น˜

  • React Virtualized๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์Šคํฌ๋กคํ•œ ์œ„์น˜๋ฅผ ๊ธฐ์–ตํ•˜๋Š” ๊ธฐ๋Šฅ์ด ์žˆ์–ด ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•ด๋„ ๊ทธ ์œ„์น˜์—์„œ ๋‹ค์‹œ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • React Window๋Š” ์Šคํฌ๋กค ์œ„์น˜ ์ €์žฅ ๊ธฐ๋Šฅ์ด ์—†์–ด, ์ƒˆ๋กœ๊ณ ์นจ ํ›„ ์ตœ์ƒ๋‹จ ํ•ญ๋ชฉ์œผ๋กœ ๋Œ์•„๊ฐ€๋Š” ๋ถˆํŽธํ•จ์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, 1000๊ฐœ์˜ ๋ฆฌ์ŠคํŠธ ํ•ญ๋ชฉ ์ค‘ 700๋ฒˆ์งธ ํ•ญ๋ชฉ์„ ๋ณด๊ณ  ์žˆ๋‹ค๊ฐ€ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ์ตœ์ƒ๋‹จ์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ๋ผ์šฐํŒ…์œผ๋กœ ์ธํ•ด ํŽ˜์ด์ง€ ๊ฐ„ ์ด๋™ ์‹œ ์ƒํƒœ๊ฐ€ ์œ ์ง€๋˜์ง€๋งŒ, URL์ด ๋ณ€๊ฒฝ๋˜๋ฉด ์Šคํฌ๋กค ์œ„์น˜๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์–ด ํŽ˜์ด์ง€๊ฐ€ ์–ธ๋งˆ์šดํŠธ๋˜๊ณ  ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ดˆ๊ธฐ ์ƒํƒœ๋กœ ๋Œ์•„๊ฐ‘๋‹ˆ๋‹ค.
  • ์–ธ๋งˆ์šดํŠธ ์‹œ API ์š”์ฒญ์ด ๋‹ค์‹œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, Context API, Redux, React Query ๋“ฑ์˜ ์ƒํƒœ ๊ด€๋ฆฌ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ถˆํ•„์š”ํ•œ API ์š”์ฒญ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋†’์ด ๊ณ„์‚ฐ (์ •์ , ๋™์ )

  • React Window๋Š” ์ •์ ์ธ ๋†’์ด๋ฅผ ์ง€์›ํ•˜๋ฏ€๋กœ ๋ฆฌ์ŠคํŠธ ํ•ญ๋ชฉ์˜ ๋†’์ด๊ฐ€ ์ผ์ •ํ•ด์•ผ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • React Virtualized๋Š” ๋™์ ์ธ ๋†’์ด๋ฅผ ์ง€์›ํ•˜์—ฌ ๋ฆฌ์ŠคํŠธ ํ•ญ๋ชฉ์˜ ๋‚ด์šฉ์— ๋”ฐ๋ผ ๋†’์ด๊ฐ€ ๋‹ฌ๋ผ์ ธ๋„ ์ž๋™์œผ๋กœ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด CellMeasurer ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ์ปดํฌ๋„ŒํŠธ๋Š” ์ƒˆ๋กœ์šด ์ฝ˜ํ…์ธ ๊ฐ€ ๋‚˜ํƒ€๋‚  ๋•Œ ๋†’์ด๋ฅผ ์ž๋™์œผ๋กœ ์žฌ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
  • CellMeasurer์™€ ํ•จ๊ป˜ CellMeasurerCache๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋†’์ด๋ฅผ ์บ์‹œํ•˜์—ฌ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

React Window

 

  • ๋ชฉ์ : ๋งŽ์€ ๋ฐ์ดํ„ฐ ๋ฆฌ์ŠคํŠธ์™€ ํ‘œ ํ˜•์‹ ๋ฐ์ดํ„ฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๋ Œ๋”๋งํ•˜์—ฌ ๋ทฐํฌํŠธ์— ๋ณด์ด๋Š” ํ•ญ๋ชฉ๋งŒ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
  • ์ฃผ์š” ๊ธฐ๋Šฅ:
    • React Window๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ •์ ์ธ ๋†’์ด๋ฅผ ๊ฐ€์ง„ ๋ฆฌ์ŠคํŠธ์™€ ๊ทธ๋ฆฌ๋“œ๋งŒ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ํ•„์š”ํ•œ ์ตœ์†Œํ•œ์˜ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์—ฌ ๊ฒฝ๋Ÿ‰ํ™”๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.
    • DOM ๋…ธ๋“œ ์ˆ˜๋ฅผ ์ตœ์†Œํ™”ํ•˜์—ฌ ๋ Œ๋”๋ง ์‹œ๊ฐ„๊ณผ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ์ค„์ž…๋‹ˆ๋‹ค.
    • ์ดˆ๋ณด์ž๋ฅผ ์œ„ํ•ด ์„ค๊ณ„๋œ ๊ฐ„๋‹จํ•œ API๋กœ ์„ฑ๋Šฅ์— ์ค‘์ ์„ ๋‘ก๋‹ˆ๋‹ค.
    • react-window-infinite-loader์™€ ๊ฐ™์€ ์™ธ๋ถ€ ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ์˜ ์ง€์—ฐ ๋กœ๋”ฉ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ๋ฒ•: ์ตœ์†Œํ•œ์˜ ์„ค์ •์œผ๋กœ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์‚ฌ์šฉ์ž ์ •์˜ ์†์„ฑ๊ณผ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋˜ํ•œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

 

React Virtualized

 

  • ๋ชฉ์ : React Window์™€ ์œ ์‚ฌํ•˜์ง€๋งŒ ๋” ๋ณต์žกํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ์œ„ํ•œ ํญ๋„“์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • ์ฃผ์š” ๊ธฐ๋Šฅ:
    • React Virtualized๋Š” ๊ฐ€์ƒํ™”๋œ ๊ทธ๋ฆฌ๋“œ, ํ•„ํ„ฐ๋ง, ์ •๋ ฌ, ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ ๋“ฑ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
      • ๊ฐ€์ƒํ™”๋œ ๊ทธ๋ฆฌ๋“œ๋Š” ๋™์ ์ธ ๋†’์ด ๊ณ„์‚ฐ์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด๊ธฐ ์ข‹์€ ํ˜•ํƒœ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐฐ์น˜๋ฉ๋‹ˆ๋‹ค.
      • ํ•„ํ„ฐ๋ง ๊ธฐ๋Šฅ์€ ํŠน์ • ์กฐ๊ฑด์— ๋งž๋Š” ํ•ญ๋ชฉ๋งŒ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      • ์ •๋ ฌ ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ์›ํ•˜๋Š” ๊ธฐ์ค€์œผ๋กœ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ •๋ ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      • ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ๋ฆฌ์ŠคํŠธ ํ•ญ๋ชฉ์˜ ์ˆœ์„œ๋ฅผ ์‰ฝ๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      • ์ผ๋ถ€ ์Šคํƒ€์ผ์— CSS๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ, ๋ฒˆ๋“ค ํฌ๊ธฐ๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•ด ํŠน์ • ๊ตฌ์„ฑ ์š”์†Œ๋งŒ ์ž„ํฌํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      • ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด shallowCompare๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ props๋‚˜ state๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์œผ๋ฉด ๋‹ค์‹œ ๋ Œ๋”๋งํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ๋ฒ•: ๋ณต์žกํ•œ API ๋•Œ๋ฌธ์— ์„ค์ •์ด ๋” ๋ณต์žกํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, react-window๋‚˜ react-virtuoso์™€ ๋น„๊ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฒˆ๋“ค ํฌ๊ธฐ๋ฅผ ์ฆ๊ฐ€์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

React Virtuoso

  • ๋ชฉ์ : ๊ฐ€๋ณ€ ํฌ๊ธฐ ํ•ญ๋ชฉ์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉฐ, ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ํฌ๊ด„์ ์ธ ๊ตฌ์„ฑ ์š”์†Œ ์„ธํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • ์ฃผ์š” ๊ธฐ๋Šฅ:
    • ์ˆ˜๋™ ํฌ๊ธฐ ์กฐ์ • ์—†์ด ๊ฐ€๋ณ€ ํฌ๊ธฐ ํ•ญ๋ชฉ์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
    • ์ฑ„ํŒ… ๋ฉ”์‹œ์ง€ ๋ฆฌ์ŠคํŠธ
    • ์Šคํ‹ฐํ‚ค ํ—ค๋”๊ฐ€ ์žˆ๋Š” ๊ทธ๋ฃนํ™”๋œ ํ•ญ๋ชฉ
      • ๋ฆฌ์ŠคํŠธ์˜ ํ•ญ๋ชฉ์„ ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„๋กœ ๊ทธ๋ฃนํ™” ํ•œ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค!(์ œ๊ฐ€ ํ—ท๊ฐˆ๋ ธ๋˜ ๋ถ€๋ถ„์ด๋ผ ํ’€์–ด ์ ์„๊ฒŒ์š”!) ์˜ˆ๋ฅผ ๋“ค์–ด, ์ œํ’ˆ ๋ฆฌ์ŠคํŠธ๊ฐ€ "๊ณผ์ผ"์ด๋‚˜ "์ฑ„์†Œ"์™€ ๊ฐ™์€ ์นดํ…Œ๊ณ ๋ฆฌ๋กœ ๋‚˜๋‰˜์–ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ๊ทธ๋ฃน์—๋Š” ์ƒ๋‹จ์— ๊ณ ์ •๋œ ํ—ค๋”๊ฐ€ ์žˆ์–ด ์‚ฌ์šฉ์ž๊ฐ€ ๋ฆฌ์ŠคํŠธ๋ฅผ ์Šคํฌ๋กคํ•  ๋•Œ ํ˜„์žฌ ์–ด๋–ค ๊ทธ๋ฃน์„ ๋ณด๊ณ  ์žˆ๋Š”์ง€ ์‰ฝ๊ฒŒ ์•Œ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. ์ด ์Šคํ‹ฐํ‚ค ํ—ค๋” ๊ธฐ๋Šฅ์€ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋” ๋‚˜์€ ๋งฅ๋ฝ์„ ์ œ๊ณตํ•˜์—ฌ ์‚ฌ์šฉ ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
    • ๋ฌดํ•œ ์Šคํฌ๋กค ๋“ฑ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
    • CSS ํด๋ž˜์Šค ์†์„ฑ์„ ํ†ตํ•ด ๋™์ผํ•œ ํฌ๊ธฐ์˜ ํ•ญ๋ชฉ์„ ์—ฌ๋Ÿฌ ์—ด๋กœ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ๋ฒ•: ๋ณต์žกํ•œ ๋ฆฌ์ŠคํŠธ ๋ทฐ ๊ตฌํ˜„์„ ๋‹จ์ˆœํ™”ํ•˜์—ฌ ์ง๊ด€์ ์ธ API๋กœ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฝ๊ฒŒ ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

์š”์•ฝ ๋ฐ ๋งˆ๋ฌด๋ฆฌ

  • ์„ฑ๋Šฅ: ์„ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ชจ๋‘ DOM ๋…ธ๋“œ๋ฅผ ์ค„์ด๊ณ  ๋ Œ๋”๋ง์„ ์ตœ์ ํ™”ํ•˜์—ฌ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋ฐ ์ค‘์ ์„ ๋‘ก๋‹ˆ๋‹ค.
  • ๋ณต์žก์„ฑ: React Window๊ฐ€ ๊ฐ€์žฅ ๊ฐ„๋‹จํ•˜๊ณ  ๊ฒฝ๋Ÿ‰์ด๋ฉฐ, React Virtualized๋Š” ๋” ๋งŽ์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์ง€๋งŒ ๋ณต์žก์„ฑ์ด ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. React Virtuoso๋Š” ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๊ณผ ์‚ฌ์šฉ ํŽธ์˜์„ฑ์˜ ๊ท ํ˜•์ด ์ข‹์Šต๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž ์ •์˜: ๊ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ํ•„์š”์— ๋งž๊ฒŒ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ ์˜ต์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ React Virtuoso์™€ React Virtualized๋Š” ์Šคํƒ€์ผ์ด๋‚˜ ๋ ˆ์ด์•„์›ƒ์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ฆˆํ•˜๋Š” ๋ฐ ๋” ๋งŽ์€ ์œ ์—ฐ์„ฑ์„ ์ค๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž๋“ค์€ ์ž์‹ ๋งŒ์˜ ๋””์ž์ธ ์š”๊ตฌ์— ๋งž์ถฐ ํ•ญ๋ชฉ์˜ ํฌ๊ธฐ, ์ƒ‰์ƒ, ๋ฐฐ์น˜ ๋“ฑ์„ ์ž์œ ๋กญ๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 
  • React Window๋Š” ๊ธฐ๋ณธ์ ์ธ ๋ฆฌ์ŠคํŠธ์™€ ๊ทธ๋ฆฌ๋“œ, ์ •์ ์ธ ๋†’์ด๋ฅผ ์ง€์›ํ•˜๋ฉฐ, ํ•„์š” ์—†๋Š” ๊ธฐ๋Šฅ๋“ค์€ ์ œ๊ฑฐํ•˜์—ฌ ๊ฐ€๋ณ๊ณ  ๊ฐ„๋‹จํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž‘์€ ํ”„๋กœ์ ํŠธ์— ์ ํ•ฉํ•˜์ง€๋งŒ, ๊ธฐ๋Šฅ์ด ์ œํ•œ์ ์ž…๋‹ˆ๋‹ค.
  • React Virtualized๋Š” ๋งŽ์€ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๋ฉฐ, ๋ณต์žกํ•œ UI/UX๋ฅผ ๊ตฌ์ถ•ํ•  ๋•Œ ์œ ์šฉํ•˜์ง€๋งŒ, ์„ค์ •์ด ๋ณต์žกํ•˜๊ณ  ํŒŒ์ผ ํฌ๊ธฐ๊ฐ€ ํฐ ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • React Virtuoso๋Š” ๊ฒฝ๋Ÿ‰ํ™”์™€ ๊ฐ„ํŽธํ•จ์„ ์ถ”๊ตฌํ•˜๋ฉฐ, ์ตœ๊ทผ ์ธ๊ธฐ๋ฅผ ๋Œ๊ณ  ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์ด ๋งŽ๋‹ค๋ฉด ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

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

 

 

๐Ÿ“ƒ ์ฐธ๊ณ  ๋ฌธํ—Œ  
React Window ๊นƒํ—™
React Virtualized ๊นƒํ—™
React Virtuoso ๊นƒํ—™
react-virtualized vs react-virtuoso vs react-window (npm-trends)
Which is Better Virtual Scrolling Libraries? (npm-compare)

 

+ Recent posts