MVVM ํŒจํ„ด

๋ฐ•์žฌํ˜„ยท2022๋…„ 4์›” 11์ผ
0

FE ํ†บ์•„๋ณด๊ธฐ

๋ชฉ๋ก ๋ณด๊ธฐ
3/10
post-thumbnail

๐Ÿ“–ย ๋ชฉ์ฐจ
1. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๋กœ์ง
2. MVVM์ด๋ž€?
3. Recoil์„ ์ ‘๋ชฉ์‹œํ‚จ MVVM ํŒจํ„ด

์ด๋ฒˆ์— collusic-new, about ํ”„๋กœ์ ํŠธ์— mvvm ํŒจํ„ด์„ ์ ์šฉํ•˜๋ฉด์„œ ๋””์ž์ธ ํŒจํ„ด์— ๋Œ€ํ•ด ์ฒ˜์Œ ๊ณต๋ถ€ํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. model, view, viewmodel์ด๋ž€ ๊ฐ๊ฐ์˜ ์šฉ์–ด๋„ ์ƒ์†Œํ–ˆ์ง€๋งŒ, ๋ฌด์—‡๋ณด๋‹ค mvvm์„ react์— ์ ์šฉ์‹œํ‚ค๋Š” ๋ฐฉ์‹์ด ์˜ˆ์ œ๋งˆ๋‹ค ๋„ˆ๋ฌด ๋‹ฌ๋ผ์„œ ํ˜ผ๋ž€์Šค๋Ÿฌ์› ๋‹ค. ๋˜ํ•œ, ๋ทฐ๋กœ์ง๊ณผ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์ •ํ™•ํžˆ ์–ด๋–ค ์ฐจ์ด๊ฐ€ ์žˆ๋Š”์ง€ ์™€๋‹ฟ์ง€ ์•Š์•„ ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋Š” ๋„์ค‘, ์ฝ”๋“œ๋ฅผ ๋ถ„๋ฆฌํ•˜๋ฉด์„œ๋„ ๊ฐœ์šด์น˜๊ฐ€ ๋ชปํ–ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์˜ค๋Š˜ ๋‚ด์šฉ์€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๋กœ์ง(๋ทฐ ๋กœ์ง๊ณผ ๋น„์Šทํ•œ ๊ฐœ๋…์ธ ๊ฒƒ ๊ฐ™๋‹ค...)๋ถ€ํ„ฐ ์–˜๊ธฐํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

1. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๋กœ์ง

๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง?

์œ„ํ‚คํ”ผ๋””์•„์—์„œ ์ •์˜ํ•˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

๋น„์ง€๋‹ˆ์Šค ๋กœ์ง(Business Logic) ๋˜๋Š” ๋„๋ฉ”์ธ ๋กœ์ง(Domain Logic)์€ํ˜„์‹ค ์„ธ๊ณ„์—์„œ ์–ด๋–ป๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค๊ณ  ์ €์žฅํ•˜๊ณ  ๋ฐ”๊ฟ€ ๊ฒƒ์ธ์ง€์— ๋Œ€ํ•œ ๋น„์ง€๋‹ˆ์Šค ๊ทœ์น™(Business Rules)์„ ์ธ์ฝ”๋“œ(Encodes) ํ•œ,์†Œํ”„ํŠธ ์›จ์–ด ์•ˆ์˜ ํ”„๋กœ๊ทธ๋žจ์˜ ํ•œ ๋ถ€๋ถ„์ด๋‹ค.
โ€”์œ„ํ‚คํ”ผ๋””์•„(์˜๋ฌธํŒ),ย Business Logic

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

ํŠน์ • ์ฑ…์— ๋Œ€ํ•œ ์ƒ์„ธ์ •๋ณด๋ฅผ Readํ•˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๊ฐ€์ง„ detailBook(bookId) ๋งŒ๋“ค์–ด๋ณด์ž.

async detailBook(bookId: number) {
    const { data } = await axios.get(`api/books/${bookId}`);
    return data;
  }

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

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

public addFavoriteList(bookId: number, bookTitle: string) {
    this.favoriteList.push({ bookId, bookTitle });
}

public getFavoriteList() {
    return this.favoriteList;
}

ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๋กœ์ง?

์œ„ํ‚คํ”ผ๋””์•„์—์„œ ์ •์˜ํ•˜๋Š” ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๋กœ์ง์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๋กœ์ง์€ ๋น„์ง€๋‹ˆ์Šค ์˜ค๋ธŒ์ ํŠธ(๋น„์ง€๋‹ˆ์Šค ๋กœ์ง)๊ฐ€ ํŒ์—… ํ™”๋ฉด๊ณผ ๋“œ๋กญ ๋‹ค์šด ๋งค๋‰ด ์ค‘์–ด๋–ค ๊ฒƒ์„ ์„ ํƒํ•  ๊ฒƒ์ธ์ง€์™€ ๊ฐ™์ด์†Œํ”„ํŠธ์›จ์–ด ์‚ฌ์šฉ์ž์—๊ฒŒ ํ‘œ์‹œ๋˜๋Š” ๋กœ์ง์„ ๋งํ•œ๋‹ค. ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๋กœ์ง์—์„œ ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์€์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ ๋ฐ ํ”„๋ ˆ์  ํ…Œ์ด์…˜๊ณผ ์ปจํ…์ธ ๋ฅผ ๋ถ„๋ฆฌํ•˜๋ คํ•˜๋Š” ์‚ฌ๋ก€์˜(an instance of) ์ค‘์š”ํ•œ ๊ด€์‹ฌ์‚ฌ์ด๋‹ค.
โ€”์œ„ํ‚คํ”ผ๋””์•„(์˜๋ฌธํŒ),ย Presentation Logic

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

๊ธฐ์—ฌ์ž‘ ๋ชฉ๋ก์„ ๋ณด์—ฌ์ค„ ๋•Œ ์‚ฌ์šฉ์ž์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„์ด ์žˆ์œผ๋ฉด ๊ทธ ์‚ฌ์ง„์„ ํ”„๋กœํ•„๋กœ, ์•„๋‹ˆ๋ฉด ๋ฏธ๋ฆฌ ์ €์žฅํ•ด๋†“์€ defaultProfile๋กœ ๋ณด์—ฌ์ฃผ๋Š” ๋ทฐ ๋กœ์ง์„ ๋งŒ๋“ค์–ด๋ณด์ž.

{contributeList.map((project, idx) => (
  <img
    src={
      project.userProfile !== undefined
        ? project.userProfile
        : `../../assets/defaultProfile/defaultProfile.png`
    }
    alt={project.userEmail}
    className="profile"
  />
))}

2. MVVM์ด๋ž€?

๋ณธ๊ฒฉ์ ์œผ๋กœ MVVM ํŒจํ„ด์— ๋Œ€ํ•œ ์„ค๋ช…์„ ํ•˜๋ ค๊ณ  ํ•œ๋‹ค. ๋จผ์ € MVVM ํŒจํ„ด์€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๋กœ์ง์„ UI๋กœ ๋ถ€ํ„ฐ ๋ถ„๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์–ด์กŒ๋‹ค. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๋กœ์ง์„ UI๋กœ๋ถ€ํ„ฐ ๋ถ„๋ฆฌํ•˜๊ฒŒ ๋˜๋ฉด ์œ ์ง€๋ณด์ˆ˜, ์žฌ์‚ฌ์šฉ, ํ…Œ์ŠคํŠธ๊ฐ€ ์‰ฌ์›Œ์ง„๋‹ค.

MVVM์€ Model, View, ViewModel์˜ ์•ฝ์ž์ด๋‹ค. ๊ฐ๊ฐ์˜ ์—ญํ• ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๋„๋ก ํ•˜์ž.

Model

  • ํ”„๋กœ๊ทธ๋žจ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์‹ค์ œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์žˆ๊ณ , api๋ฅผ ์š”์ฒญํ•˜๊ฑฐ๋‚˜, ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์žˆ๋‹ค. ์ฆ‰, ์‚ฌ์šฉํ•  ๋ฐ์ดํ„ฐ์— ๊ด€๋ จ๋œ ๋™์ž‘๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฌ๋‹ค.
class BookModel {
    constructor() {
      books = [
        {id: 'RCB-123',name: "React Cook Book", isFavorite: false},
        {id: 'VCB-123',name: "Vue Cook Book", isFavorite: false},
        {id: 'ACB-123',name: "Angular Cook Book", isFavorite: false}
      ];
    }
    
    getBooks() {
        return this.books
    }

    toggleFavorite(bookId) {
      const target = this.books.filter(item => item.id === bookId)[0];
      target.isFavorite = !target.isFavorite
    }
}

View

  • ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ง€๋Š” ๋ถ€๋ถ„์œผ๋กœ UI์™€ ๊ด€๋ จ๋œ ๊ฒƒ์„ ๋‹ค๋ฃจ๋Š” ๋ถ€๋ถ„์ด๋‹ค.
  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€ ์—†๊ณ , ๋ทฐ ๋กœ์ง๋งŒ์ด ์กด์žฌํ•œ๋‹ค.
  • View๋Š” ViewModel๋ฅผ ์ง€์†์ ์œผ๋กœ ๊ด€์ฐฐํ•œ๋‹ค.

ViewCotroller์„ ๋งŒ๋“ค์–ด View์—์„œ ๋ทฐ ๋กœ์ง์„ ๋ถ„๋ฆฌ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋Š”๋ฐ, ๋” ํ—ท๊ฐˆ๋ฆด ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์•„๋ž˜ ์˜ˆ์ œ์—์„œ๋Š” ๋ถ„๋ฆฌ์‹œํ‚ค์ง€ ์•Š๋„๋ก ํ•˜๊ฒ ๋‹ค.

const BookView = ({bookViewModel}) => { 
    const [isNeverView, setIsNeverView] = useState(false);

    const handleToggleFavorite = useCallback((bookId) => {
      bookViewModel.toggleFavorite(bookId)
    }, [viewModel]);

    const handleClickNeverView = useCallback(() => {
      setIsNeverView(!isNeverView);
    }, [isNeverView]);
    
    return (
      <BookList 
        books={bookViewModel.getBooks()} 
        handleToggleFavorite={handleToggleFavorite}
        handleClickNeverView={handleClickNeverView}
      />
    )
}

ViewModel

  • View์™€ Model์„ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ์—ญํ• ์ด๋‹ค.
  • Model์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜๊ฑฐ๋‚˜ View์—์„œ Model์— ์žˆ๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๊ฒฝ์šฐ ๊ทธ๋กœ์ธํ•œ ๋ชจ๋“  ๋ณ€๊ฒฝ ์‚ฌํ•ญ๋“ค์ด ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธ ๋˜์–ด View์— ์ ์šฉ๋œ๋‹ค.
  • View Model์€ View ์™€ 1:N์˜ ํ˜•ํƒœ๋ฅผ ์ด๋ฃจ๊ณ  ์žˆ๋‹ค.
class BookViewModel {
    constructor(bookStore) {
        this.store = bookStore
    }

    getBooks() {
        return this.store.getBooks()
    }

    toggleFavorite(bookId) {
       this.store.toggleFavorite(bookId)
    }
}

View โ†”ย ViewModel, ViewModel โ†”ย Model ์ด์–ด์ฃผ๊ธฐ

  • ViewModel์„ ์ธ์Šคํ„ด์Šคํ™”ํ•˜๊ณ  ํ•„์š”ํ•œ ๋ชจ๋“  ์˜์กด์„ฑ ์ฃผ์ž…์„ ๋‹ด๋‹นํ•œ๋‹ค.
  • ViewModel์˜ ์ธ์Šคํ„ด์Šค๋Š” props๋ฅผ ํ†ตํ•ด View๋กœ ์ „๋‹ฌ๋œ๋‹ค.

Provider๋ฅผ ๋งŒ๋“ค์–ด ์„œ๋กœ๋ฅผ ์ด์–ด์ฃผ๋Š” ๊ตฌ์„ฑ์ฒด๋ฅผ ํ˜•์„ฑํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์•„๋ž˜ ์˜ˆ์ œ์—์„œ๋Š” ๋ถ„๋ฆฌ์‹œํ‚ค์ง€ ์•Š๋„๋ก ํ•˜๊ฒ ๋‹ค.

import React from 'react';

import BookViewModel from './viewModel/BookViewModel'
import BookModel from './model/BookModel'
import BookView from './view/BookView'

function App() {
  const bookModel = new BookModel()
  const bookViewModel = new BookViewModel(bookModel)

  return (
    <>
      <BookView 
        bookViewModel={bookViewModel}
      />
    </>
  )
}

export default App;

3. Recoil์„ ์ ‘๋ชฉ์‹œํ‚จ MVVMํŒจํ„ด

recoil์€ state๋ฅผ ๊ตฌ๋…ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์— ํ•œํ•˜์—ฌ state๊ฐ€ ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ๋ฆฌ๋ Œ๋”๋ง์ด ๋˜๋Š” ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. recoil์— ๊ด€ํ•ด์„œ๋Š” ๋‹ค์Œ์— ์ž์„ธํžˆ ์„ค๋ช…ํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

Recoil๋กœ MVVM์„ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์ œ๋Š” ๋งŽ์ง€๊ฐ€ ์•Š๋‹ค. ์˜ˆ์ œ๋ฅผ ๋”ฐ๋ผํ•˜๋ฉด์„œ ViewModel์—์„œ ๋ทฐ ๋กœ์ง์ด ๋“ค์–ด๊ฐ€๋ฉด์„œ View์™€ ViewModel์˜ ๊ตฌ๋ถ„์ด ํ™•์‹ค์น˜ ์•Š๋‹ค๋Š” ๋Š๋‚Œ์„ ๋ฐ›์•˜๋‹ค. ์•„๋ž˜์—์„œ๋Š” Recoil์ด ์ ์šฉ๋œ Model๊ณผ ๋‚ด๊ฐ€ ์„์—ฐ์น˜ ์•Š๊ฒŒ ์ƒ๊ฐํ–ˆ๋˜ ๋ถ€๋ถ„์— ๋Œ€ํ•ด ์–˜๊ธฐํ•ด๋ณด๋ ค ํ•œ๋‹ค.

Model

recoil์—์„œ๋Š” ๋ฐ์ดํ„ฐ์˜ ์ƒํƒœ๋ฅผ atom์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ณ , atom์˜ ์ƒํƒœ๊ฐ’์„ ์ด์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๊ณตํ•œ ์ƒํƒœ๋ฅผ selector๋กœ ๊ด€๋ฆฌํ•œ๋‹ค. CRUD์™€ ๋ฐ์ดํ„ฐ ๊ฐ€๊ณต์— ๋Œ€ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋ชจ๋‘ recoil์˜ atom, selector๋ฅผ ํ†ตํ•ด ์ด๋ฃจ์–ด์ง„๋‹ค.

export const AllCheckProductItemsSelector = selector({
    key: "ProductList/Item/AllCheck",
    get: ({ get }) => {
        const productList = get(ProductListAtom)
        if (productList) return false;
        return get(ProductListAtom).every(item => item.checked)
    },
    set: ({ set }) => {
        set(ProductListAtom, prevState => prevState.map(item => {
                const currentItem = { ...item };
                currentItem.checked = !currentItem.checked;
                return currentItem;
            })
        )
    }
})

export const CheckProductItemSelector = selector({
    key: "ProductList/Item/Check",
    get: ({ get }) => { },
    set: ({ get, set }, product_id) => {
        set(ProductListAtom, prevState => {
            return prevState.map(item => {
                 const currentItem = {...item}
                    if (item.id === product_id) {
                        currentItem.checked = !currentItem.checked
                    }
                return currentItem;
            })
        })
    }
})

ViewModel์—์„œ ๋ทฐ๋กœ์ง์ด??

MVVM์˜ˆ์ œ์—์„œ ViewModel์€ ์˜์กด์„ฑ ์ฃผ์ž…์„ ํ†ตํ•ด View์™€ Model์„ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•œ๋‹ค. ์ด ๊ตฌ์„ฑ์ฒด์—์„œ๋Š” ๋ทฐ๋กœ์ง๋„, ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๋„ ๋“ค์–ด๊ฐ€์ง€ ์•Š๋Š”๋‹ค. ํ•˜์ง€๋งŒ ํƒ€ ๋ธ”๋กœ๊ทธ์—์„œ์˜ ViewModel์˜ˆ์ œ๋ฅผ ๋ณด๋ฉด ๋ทฐ ๋กœ์ง์ด ๋“ค์–ด๊ฐ€ ์žˆ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

const [deliveryMethodList] = useRecoilState(DeliveryMethodAtom);
const [selectedMethod] = useRecoilState(SelectedMethodAtom);
const [productList] = useRecoilState(ProductListAtom);
const price = useRecoilValue(PriceStatsSelector);
const [, toggleChecked] = useRecoilState(CheckProductItemSelector);
const [allChecked, allCheckedProductItem] = useRecoilState(
  AllCheckProductItemsSelector
);
const [, increaseAmount] = useRecoilState(
  IncreaseProductItemAmount
);
const [, decreaseAmount] = useRecoilState(
  DecreaseProductItemAmount
);
const [, handleDeleteProductItem] =
  useRecoilState(DeleteProductItem);
const [, changeDeliveryMethod] = useRecoilState(
  ChangeDeliveryMethod
);
const { isDropdown, toggleDropdown } = useDropdown();
const toggleAllChecked = () => {
  allCheckedProductItem(allChecked);
};
const handlechangeDeliveryMethod = (method_id: number) => {
  changeDeliveryMethod(method_id);
  toggleDropdown();
};
const handleOrder = () => {
  console.log("์ฃผ๋ฌธ ์™„๋ฃŒ ๋กœ์ง");
};

์•ž์œผ๋กœ collusic-new ํ”„๋กœ์ ํŠธ ๋ฆฌํŒฉํ† ๋ง์„ ๊ณผ์ •์—์„œ recoil์„ ์‚ฌ์šฉํ•œ ViewModel๊ณผ View๋ฅผ ๋งŒ๋“ค์–ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.


์ฐธ์กฐ
[ Eassy - Technology, IT, Web ] ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง(๊ทœ์น™, ์ธต) ๊ณผ ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๋กœ์ง(๊ทœ์น™, ์ธต) ์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€?
React์—์„œ MVVM ํŒจํ„ด ์•Œ์•„๋ณด๊ธฐ
๋กœ์ง์„ UI๋กœ๋ถ€ํ„ฐ ๋ถ„๋ฆฌํ•˜๋Š” MVVM Architecture Pattern
MVVM ๋””์ž์ธํŒจํ„ด
[TIL]React์™€ MVVMํŒจํ„ด
Recoil๋กœ MVVM ์‚ฌ์šฉํ•˜๊ธฐ - (1) MVVM

profile
๊ณต๋™์˜ ์„ฑ์žฅ์„ ์ถ”๊ตฌํ•˜๋Š” ๊ฐœ๋ฐœ์ž

0๊ฐœ์˜ ๋Œ“๊ธ€