ICSI301 Төсөл 5: Нэг хуудаст апп

Энэ төслөөр фото зураг хуваалцдаг вэб аппыг эхлэлийг хийхэд React сангийн Material UI компоненттой хамтад нь ашиглана. Төслийн хоёрдахь хэсэгт өгөгдлийг серверээс авах талаар судлах болно.

Тохиргоо

Та өөрийн систем дээрээ Node.js болон npm модуль менежерийг суулгасан байх ёстой. Хэрэв үгүй бол, суулгах зааврыг дагаж одоо суулгаарай.

project5 нэрээр хавтас үүсгэж энэ файлыг дотор нь задалж хуулаарай. Энэ zip файл энэ даалгаварт ашиглах гарааны файлуудыг агуулж байгаа.

Энэ даалгаварт Node.js-ийн 20.19, 22.13 эсвэл 24-өөс дээш хувилбар шаардлагатай. Мөн хэд хэдэн node модуль хэрэгтэй. Жишээ нь, Vite нь React аппын JSX файлуудыг ЖаваСкрипт болгон хөрвүүлж багцалдаг (build) багаж, ESLint нь кодыг шалгах багаж, ExpressJS нь веб аппыг веб хөтчид өгч үйлчлэх энгийн Node.js веб сервер үүсгэхэд хэрэглэгдэнэ. Мөн React Router болон React компонент, CSS загварыг (stylesheet) агуулсан Material UI модулийг татаж авна. Эдгээр модулийг project5 хавтас дотроос дараах командыг ажиллуулж суулгана:

npm install
React болон Material UI компонентууд node_modules дэд хавтас дотор суух ч гэсэн тэдгээрийг бид Node.js-ээр биш, веб хөтчид дуудаж ашиглах болно.

Төслийн бүтэц нь Төсөл 4-ийн адил Vite-ийн стандарт бүтэц дээр суурилсан. Аппын бүх эх код src хавтаст, аппыг ачаалах index.html хуудас төслийн үндсэн хавтаст, зургууд public/images хавтаст байрлана.

package.json файлын "scripts" шинжид байгаа дараах npm скриптүүдийг ажиллуулах боломжтой:

  • npm run lint - ESLint багажийг төслийн бүх ЖаваСкрипт файлд ажиллуулна. Даалгавраар илгээх таны код чинь ESLint програмыг ямар нэг санамжгүйгээр (warnings) давсан байх ёстой.
  • npm run build - vite.config.js файлд байгаа тохиргоогоор аппын бүх JSX файлуудыг багцалж dist хавтас дотор ЖаваСкрипт багц (bundle) файлууд үүсгэнэ.
  • npm run build:w - npm run build командтай адил, гэхдээ --watch горимоор ажиллана. Ингэснээр эх кодын файлуудыг хянаж, аль нэгийг нь өөрчилж хадгалах бүрт багцыг дахин үүсгэнэ. Шинэ багцыг харахын тулд веб хөтчийн хуудсаа сэргээнэ (refresh).
  • npm start - webServer.js веб серверийг асаана.

Бүх даалгаварыг гүйцэтгэсэн кодууд project5 хавтас дотор байх ёстой.

Энэ төсөлд Төсөл 4-ийн Vite хөгжүүлэлтийн серверийн (npm run dev) оронд бидний өгч буй webServer.js веб серверийг ашиглана. Учир нь Даалгавар 2-т апп өгөгдлөө энэ серверээс авна. Сервер нь dist хавтсыг үйлчилдэг тул эхлээд багцаа үүсгэх хэрэгтэй. project5 хавтас дотроос командын мөрийн хоёр цонх нээж, нэгд нь:

npm run build:w
нөгөөд нь:
npm start
командыг ажиллуулна. Ингэснээр та кодоо өөрчлөх бүрт багц автоматаар шинэчлэгдэх бөгөөд апп http://localhost:3000 хаягаар ажиллана. Серверийг зогсоохдоо командын мөрөн дээр Ctrl+C дарна.

Даалгавар 1: Фото хуваалцдаг апп үүсгэх (40 оноо)

Таны үүсгэх PhotoApp-д зориулж бид суурь бүтцэт (skeleton) гарааны кодыг өгсөн байгаа: index.html хуудас нь src/main.jsx-ийг ачаалж, тэр нь src/photoShare.jsx доторх PhotoShare компонентыг рендерлэнэ. Үүнийг http://localhost:3000 URL-ээр хандаж ажиллуулна. Энэ суурь код:

  • Хичээлээр заасан мастер-деталь хэвийг хэрэгжүүлсэн Material UI ашигладаг React веб аппыг дууддаг. Энэ апп дээд хэсэгтээ Material UI App Bar , зүүн хажуудаа UserList, контент талбайдаа UserDetail эсвэл UserPhotos компонентийг байршуулсан байгаа.
  • Нэг хуудаст аппд гүн холбоосыг (deep linking) бий болгохын тулд React Router-ийн declarative горимоор дараах гурван компонентийг заах замыг тохируулсан.
    1. /users нь src/components/UserList/UserList.jsx доторх UserList компонентийг заана.
    2. /users/:userId нь src/components/UserDetail/UserDetail.jsx доторх UserDetail компонентийг заана.
    3. /photos/:userId нь src/components/UserPhotos/UserPhotos.jsx доторх UserPhotos компонентийг заана.
    Дэлгэрэнгүйг src/photoShare.jsx дотор BrowserRouter, Routes, Route болон Outlet компонентийг хэрхэн хэрэглэж байгаагаас харж болно. Замын бүх компонент нь PhotoShareLayout хүрээний дэд зам (nested route) бөгөөд контент талбай дахь <Outlet /> байрлалд рендерлэгдэнэ.
  • src/components/* хавтас дотор байгаа компонент бүрт хоосон CSS файл болон юу хийх, модель өгөгдлийг хэрхэн хэрэглэх тухай тайлбартай энгийн функц компонентыг тодорхойлж өгсөн.

Энэ даалгаврын хувьд бид өөрсдийн шидэт icsi301models моделийг ашиглана. Энэ нь урьдаас бэлдсэн мэдээллийг дэлгэцлэхэд модель өгөгдөл болох юм. Төсөл 4-ийн адил моделийг ЖаваСкрипт модулиас импортлож хандана:

import icsi301models from '../../modelData/photoApp';
Модель src/modelData/photoApp.js файлд байгаа бөгөөд схемийг доор тайлбарласан болно.

Таны даалгавар бол өгсөн хоосон-програмыг (skeleton) хуурмаг модель өгөгдөл дээр ажилладаг болгох юм. Энэ програмд хэдийнэ UserList, UserDetail, болон UserPhotos зэрэг компонентийг холбож өгсөн байгаа. Энэ нь React Router компонентод шаардлагатай параметрүүд холбож өгсөн бөгөөд компонентийг үүсгэх ихэнх ажлуудыг хийчихсэн. Эдгээрийг дараах шаардлагын дагуу хөгжүүлнэ:

  • UserList компонент системд байгаа бүх хэрэглэгчийн дэлгэрэнгүй мэдээллийг харуулахын тулд хэрэглэгчдийг жагсааж харуулдаг байна. Компонент нь хажуу хэсэгт (side bar) байрлах ба хэрэглэгчийн нэрсийн жагсаалтыг дэлгэцлэнэ. Хэрэглэгчийн нэр дээр дарахад тухайн хэрэглэгчийн дэлгэрэнгүй мэдээллийг үндсэн хэсэгт (content area) дэлгэцэлдэг байна.
  • UserDetail компонент нь URL-ийн userId параметрийг React Router-ийн useParams() hook-оор авна. Энэ харагдац нь хэрэглэгчийн дэлгэрэнгүйг харуулах бөгөөд тухайн хэрэглэгчийн оруулсан зургуудыг харуулах UserPhotos компонентруу заах линктэй байна.
  • UserPhotos компонент мөн userId-ийг useParams() hook-оор авна. Сонгосон хэрэглэгчийн бүх зургийг харуулна. Тухайн хэрэглэгчтэй холбоотой бүх зургийг дэлгэцлэх ёстой. Зураг бүрийн хувьд бичсэн сэтгэгдлүүдийг харуулна. Сэтгэгдэл нэг бүрийн бичсэн огноо, тухайн сэтгэгдлийг бичсэн хэрэглэгч болон сэтгэгдлийн бичвэрийг харуулдаг байна. Тухайн сэтгэгдлийг бичсэн хэрэглэгчийн нэр нь линк байх ба дарахад тухайн хэрэлэгчийн дэлгэрэнгүй мэдээллийг үзүүлнэ.

Эдгээр компонентоос гадна src/components/TopBar/TopBar.jsx дотор байгаа TopBar компонентийг дараах шаардлагын дагуу шинэчлэнэ:

  • TopBar-ын зүүн талд таны нэр байна.
  • TopBar-ын баруун талд аппын агуулгыг үндсэн хэсэгт юу үзүүлж байгаагаас хамаарч гаргана. Жишээ нь, үндсэн хэсэгт хэрэглэгчийн дэлгэрэнгүй мэдээллийг харуулж байвал TopBar нь хэрэглэгчийн нэрийг, хэрэв хэрэглэгчийн зургийг үзүүлж байвал "Зургууд: " гээд араас нь хэрэглэгчийн нэрийг залгаж харуулдаг байна. TopBar нь <Routes>-ээс гадна рендерлэгддэг тул useParams() ажиллахгүй. Оронд нь useLocation() эсвэл useMatch() hook-ийг ашиглаж болно.

Энэ хоосон-програмд React Router ашиглан аппын ялгаатай харагдац бүрт гүн-холбоос (deep-linking) үүсгэж өгсөн. Таны үүсгэх компонент энэ боломжийг эвдэхгүй байх ёстой. Энэ нь веб хөтчийг сэргээх (refresh) үед өмнө үзэж байснаараа гардаг байх боломжийг олгож байгаа юм. BrowserRouter нь /users/:userId гэх мэт жинхэнэ URL ашигладаг тул сэргээх үед ийм хүсэлт серверт ирдэг. Үүнд зориулж webServer.js нь өөр замд тохироогүй бүх хүсэлтэд аппын index.html-ийг буцаадаг. Бидний компонент үүсгэдэг стандарт арга нь гүн-холбоосыг автоматаар тохируулдаг. Компонентууд хооронд объектыг дамжуулахад үүнийг анхаарч байх ёстой. Хэрэв ямар нэг зүйл эвдсэн байвал харагдац бүр дээр сэргээх туршилт хийгээд үзээрэй.

Харагдах байдлын хувьд цаг их зарцуулах хэрэггүй, энэ нь аятайхан, ойлгомжтой байх ёстой. Мэдээллийг дэлгэцлэх зохиомж нь цэвэр байна (Жишээ нь, зураг бүрийн хувьд холбогдох комментууд нь ойлгомжтой байх).

Фото апп модель өгөгдөл

Энэ даалгаврын хувьд бид src/modelData/photoApp.js модулиас импортолдог шидэт модель өгөгдлийг ашиглана. Модель нь дөрвөн төрлийн объекттой. Эдгээр нь user, photo, comment, болон SchemaInfo төрөл.

  • Фото хуваалцдаг сайт дээрх фотонуудыг хэрэглэгчээр нь зохион байгуулсан. Бид хэрэглэгчийг user объектоор илэрхийлэх ба дараах шинжүүдтэй:
    _id: Хэрэглэгчийн ID.
    first_name: Хэрэглэгчийн өөрийн нэр.
    last_name: Хэрэглэгчийн овог.
    location: Хэрэглэгчийн байршил.
    description: Хэрэглэгчийн талаарх товч тайлбар.
    occupation: Хэрэглэгчийн ажил мэргэжил.
    icsi301models.userModel(user_id) функц нь хэрэглэгчийн user_id ID-аар user объектыг буцаадаг. Харин icsi301models.userListModel() функц нь аппын бүх хэрэглэгчийн user объекыг буцаадаг.
  • Хэрэглэгч бүр олон фото серверт хуулах боломжтой. Хэрэглэгчийн нэг зургийг photo объектоор, дараах шинжтэйгээр илэрхийлдэг:
    _id: Фото зургийн ID.
    user_id: Фото зургийг үүсгэсэн хэрэглэгчийн user ID.
    date_time: Фото зургийг өгөгдлийн санд нэмсэн огноо ба цаг.
    file_name: Фото зургийг агуулж байгаа файлын нэр (project5/public/images хавтас доторх; хөтчөөс /images/<file_name> замаар хандана).
    comments: Фото зургийн бүх сэтгэгдлийг comments хадгалсан объектон хүснэгтэн хувьсагч, Нэг comment нь тухайн фото зураг дээр бичигдсэн сэтгэгдэл.
    icsi301models.photoOfUserModel(user_id) функц нь хэрэглэгчийн user_id-д холбогдох photo объектуудыг агуулсан хүснэгтэн хувьсагчийг буцаадаг.
  • Фото бүр олон сэтгэгдэлтэй байж болно (ямар ч хэрэглэгч ямар ч фото зурагт сэтгэгдэл бичиж болно). Сэтгэгдэл comment объект дараах шинжүүдтэй:
    _id: Сэтгэгдлийн ID.
    photo_id: Сэтгэгдэл харгалзах photo фото зургийн ID.
    user: Сэтгэгдэл бичсэн хэрэглэгчийн user объект.
    date_time: Сэтгэгдлийг бичсэн огноо ба цаг.
    comment: Сэтгэгдлийн бичвэр.
  • Турших зорилгоор SchemaInfo объектыг дараах шинжээр үүсгэсэн:
    _id: SchemaInfo схемийн ID.
    __v: SchemaInfo объектын хувилбарын дугаар.
    load_date_time: SchemaInfo ачаалах үеийн огноо ба цаг. Нэг тэмдэгт мөр.

Даалгавар 2: Веб серверээс моделийн өгөгдлийг авах (20 оноо)

Даалгавар 1-ийг гүйцэтгэсэний дараа манай фото аппын front-end бодит апп шиг харагдах ёстой. Гол асуудал нь хуурмаг модель өгөгдлийг аппын ЖаваСкрипт багц дотор шууд импортолж ачаалсан байсан. Даалгавар 2-ын хувьд, бид энэ арга залийг хаяад бодит апптай ижил серверээс модель өгөгдөл авдаг болгоно.

Энэ төслөөр өгсөн webServer.js нь Даалгавар 1-д импортолж ашигласан icsi301models-ийг ExpressJS чиглүүлэгч (route) ашиглан серверээс өгөгдөл авах боломжийг олгож байгаа. Энэ API-ыг webServer.js-ээр гаргаж байгаа ба харгалзах URL хаягаар HTTP GET хүсэлт явуулахад icsi301models моделиудаас өгөгдлийг буцааж өгдөг. Эдгээр GET хүсэлтэд хариулах HTTP хариулт нь JSON форматтай байна. Энэ API бол:

  • /test/info - icsi301models.schemaInfo() объектыг буцаана. Энэ нь URL нь модель дуудаж турших зориулалттай.
  • /user/list - icsi301models.userListModel() функцийг дуудаж буцаана..
  • /user/:id - icsi301models.userModel(id) функцийг дуудаж буцаана.
  • /photosOfUser/:id - icsi301models.photoOfUserModel(id) функцийг дуудаж буцаана..

Дээрх URL-ийг веб хөтөчийн хаягийн мөр дээрээ бичээд бодитоор хэрхэн ажиллаж байгааг харж болно. Жишээ нь, http://localhost:3000/test/info болон http://localhost:3000/user/list линк нь веб хөтчийн цонх дээр моделийн өгөгдлийг JSON форматаар харуулна.

Веб сервер дээрээс моделийг авдаг болгож програмаа хөрвүүлэхийн тулд src/lib/fetchModelData.js файлд байгаа fetchModel функцийг хөгжүүлэх ёстой. Энэ функцийг дараах тодорхойлолтын дагуу хөгжүүлнэ:

/**
 * fetchModel - Веб серверээс моделийг авдаг
 *
 * @param {string} url     GET хүсэлт явуулах URL
 *
 * @returns GET хүсэлтийн хариултад ирэх JSON объектоор дүүргэсэн Promise буцаана. 
 * Өгөгдөл нь нэг объектын "data" нэртэй шинжэд байна.
 * Хэрэв хүсэлт явуулахад алдаа гарвал Promise нь дараах шинжүүдийг агуулсан объектыг буцаана.
 * {number} status          HTTP хариултын төлөв
 * {string} statusText      xhr хүсэлтээр ирсэн өгөгдөл
 */

Хэдийгээр энэ функцийг олон янзын бэлэн хөгжүүлсэн модуль ашиглан хийж болох ч та AJAX-ийн доод түвшний мэдлэгт суралцах хэрэгтэй. Та fetchModel функцийг кодлохдоо өөр бусад санг ашиглахгүй байх; XMLHttpRequest DOM объектыг үүсгэх болон түүний үзэгдлийг барьж авч хэрэглэх ЖаваСкрипт кодыг бичээрэй.

Таны код олон fetchModel хүсэлтүүдийг хүлээн авч хариулдаг байх ёстой. Өөрийн fetchModel функцийг ажиллаж байгаа эсэхийг харахын тулд http://localhost:3000 хаягаар орж үзээрэй. Энэ нь http://localhost:3000/test/info хаягруу хүсэлт явуулаад буцаж ирэх хувилбарын дугаарыг дэлгэцэлдэг юм. Хувилбарын дугаарыг өөрийн аппынхаа TopBar компонентод дэлгэцэлдэг байна.

src/lib/fetchModelData.js файлд fetchModel функцийг амжилттай кодлосны дараа та дараах файлуудын кодыг өөрчлөөрэй.

  • src/components/UserDetail/UserDetail.jsx
  • src/components/UserList/UserList.jsx
  • src/components/UserPhotos/UserPhotos.jsx
Дээрх fetchModel функц нь серверээс өгөгдлийг авах хүсэлт тавьдаг. Таны компонентуудад icsi301models-ийг шууд дуудаж ашиглах код байх ёсгүй. Өөрөөр хэлбэл, src/components доторх аль ч файлд дараах мөр байх ёсгүй (webServer.js үүнийг импортлох нь зөв):
import icsi301models from '../../modelData/photoApp';

Загвар (5 оноо)

Энэ оноог MVC загварын зөвлөмжийг дагаж зөв зохион байгуулсан тохиолдолд өгнө. Нэмж хэлэхэд, таны код болон компонент цэвэрхэн, уншууртай байх ёстой ба таны апп ядаж"тодорхой хэмжээнд гоё" харагддаг, хэрэглэгддэг байна.

Бид Material UI гэх Гүүглээс гаргасан Material Design зөвлөмжийг хэрэгжүүлсэн React компонентийг ашиглана. Хичээл дээр ярьсан Master-Detail загварыг хэрэгжүүлэхэд Material UI-ийн Grid component ашиглаж харуулсан. Мөн App Bar компонентоор аппын толгой хэсгийг хийсэн байгаа. Хэдийгээр Material Design загварт бүрэн нийцсэн апп хөгжүүлэхийг шаардахгүй ч боломжтой бол Material UI элементүүдийг аль болох ашиглаж хийгээрэй.

Бас илгээхээсээ өмнө ESLint ажиллуулахаа мартуузай. ESLint ямар нэг алдаа өгөхгүй ёсгүй.

Нэмэлт оноо (5 оноо)

userPhotos компонент нь хэрэглэгчийн бүх зургаас гадна зурагт бичсэн комментуудыг харуулах ёстой. Энэ арга нь маш олон зурагтай хэрэглэгчийн хувьд тийм ч сайн ажиллахгүй. Нэмэлт онооны даалгавраар нэг зургийг комменттой нь харуулдаг болгож хөгжүүлэх ба өмнөх болон дараах гэсэн механизмыг (жишээ нь, stepper буюу алхамт байдлаар) хэрэгжүүлж хэрэглэгчийн зургийг дэлгэцэлдэг байна.

Энэ даалгаврт нэмэлт оноо авахын тулд таны шийдэл дараах шиг байна:

  • Фото аппдаа "Нэмэлт сонголт" ойлголтыг оруулж ирнэ. Апп эхлэж ажиллахад энэ сонголт нь үргэлж идэвхгүй байна. TopBar компонент "Нэмэлт сонголтыг" идэвхжүүлэх чагтлах товлууртай (checkbox) байх үүнийг чагталсан тохиолдолд "Нэмэлт сонголт" идэвхтэй болно. Эсрэг тохиолдолд идэвхгүй болно. Чагтлах товчлуур дарснаар сонголтыг идэвхтэй/идэвхгүй болгоно.
  • Таны апп "Нэмэлт сонголт"-ыг идэвхжүүлээгүй бол зураг үздэг ердийн харагдацаа ашигладаг байх юм. Хэрэв идэвхжсэн бол зургуудыг нэг нэгээр нь алхамт байдлаар харуулдаг байна.
  • Алхамаар харуулах хэрэглэгчийн интерфэйс нь хэрэглэгч зургуудын хамгийн эхнийхийг үзэж байвал бол өмнөх зурагруу, хамгийн сүүлд очсон бол дараах зурагруу шилжих боломжгүй учир үүнийг маш ойлгомжтой (жишээ нь, товчлуурыг идэвхгүй болгох) харуулдаг байна.
  • Таны апп тухайн нэг зургийн URL хаягийг веб хөтчийн хаягийн мөрөөс хуулж шэйрлэх болон тэмдэглэж авах (bookmark) боломжийг олгодог байна. Веб хөтчийн өмнөх болон дараах шилжих товч нь өмнөхрүү буцах, эргэж буцах гээд хийдэг үйлдлээ хийдэг байна. Тухайн зургийг заах гүн холбоосоор аппруу орох үед алхамт байдлаар харуулах үүргээрээ ажилладаг байна.

Санамж: Энэ нэмэлт онооны даалгаврыг хийх нь төслөөр хийсэн нэмэлт онооны бус бусад даалгавруудын хэд хэдэн хэсэгт өөрчлөх шаардлага гарна. Онцлог чагтлах (feature flag) товчлуураар ийм боломжийг хийх нь веб аппликейшнд нийтлэг практик байдаг. Гэхдээ нэмэлт онооны бус бусад даалгавруудаа эвдэж нэмэлт оноо авах гэж байгад илүү их оноо алдах эрсдэл бий. Болгоомжтой байгаарай.

Хүлээлгэн өгөх үр дүн

Ердийн Даалгавар илгээх зааврын дагуу аппаа бүхэлд нь (project5 хавтас доторх бүгд) илгээнэ. Илгээхээсээ өмнө зааврын дагуу төслийн хавтасаа цэвэрлээрэй. Ялангуяа, 180MB хэмжээтэй node_modules/ хавтсыг дэмий хадгалахыг хүсэхгүй байна. dist/ хавтсаа ч бас устгахаа мартуузай.