วิธีพัฒนาแอปโซเชียลมีเดีย: คู่มือทีละขั้นตอน

ภาพหน้าปกสำหรับการสร้างแอปโซเชียลมีเดีย

ในบทความนี้ เราจะพูดถึงโซเชียลเน็ตเวิร์ก ประเภทต่าง ๆ ของแอปโซเชียลมีเดีย ประโยชน์ และฟีเจอร์ที่จำเป็นต้องมี

นอกจากนี้ เรายังจะดูวิธีการสร้างโซเชียลเน็ตเวิร์กทีละขั้นตอน โดยจะใช้ Back4app เป็นแบ็กเอนด์ และ Next.js เป็นฟรอนต์เอนด์

Contents

โซเชียลเน็ตเวิร์กคืออะไร?

โซเชียลเน็ตเวิร์กคือแอปโซเชียลมีเดียที่เปิดให้ผู้คนเชื่อมต่อและมีปฏิสัมพันธ์กันได้

เมื่อผู้ใช้สองคนเชื่อมต่อกันแล้ว พวกเขาจะสามารถแชร์ข้อมูลผู้ใช้ พูดคุยแลกเปลี่ยนข้อความ ส่งรูปภาพ แชร์โพสต์ และอื่น ๆ อีกมากมาย

ทุกวันนี้ แอปโซเชียลมีเดียได้รับความนิยมอย่างสูง แทบทุกคนล้วนสมัครใช้งานแอปโซเชียลมีเดียอย่างน้อยหนึ่งแอป จากงานวิจัยของ Buffer แพลตฟอร์มโซเชียลมีเดียที่ได้รับความนิยมที่สุดได้แก่:

  • Facebook (2.96 พันล้านผู้ใช้งานต่อเดือน)
  • YouTube (2.2 พันล้านผู้ใช้งานต่อเดือน)
  • Instagram (2 พันล้านผู้ใช้งานต่อเดือน)
  • TikTok (1 พันล้านผู้ใช้งานต่อเดือน)
  • Snapchat (500 ล้านผู้ใช้งานต่อเดือน)

MAUs ย่อมาจาก Monthly Active Users คือจำนวนผู้ใช้งานที่มีปฏิสัมพันธ์กับแพลตฟอร์มโซเชียลมีเดียของคุณในแต่ละเดือน

แม้ว่าตลาดแอปโซเชียลมีเดียจะกว้างใหญ่มาก แต่การพัฒนาแอปโซเชียลมีเดียนั้นไม่ใช่เรื่องง่าย

นี่ถือเป็นหนึ่งในโปรเจกต์ไอทีที่ซับซ้อนที่สุด คุณอาจประเมินความยากในการสร้างแอปโซเชียลเน็ตเวิร์กต่ำเกินไป จนประสบความล้มเหลวและขาดทุนอย่างมหาศาล

ประเภทของแอปโซเชียลมีเดีย

อย่างที่ได้กล่าวไปในส่วนก่อนหน้า โซเชียลเน็ตเวิร์กเป็นเพียงหนึ่งในประเภทของแอปโซเชียลมีเดีย ประเภทอื่น ๆ ได้แก่:

  • Media-sharing networks (Instagram, TikTok, YouTube)
  • Content-sharing networks (Pinterest, Tumblr, Flickr)
  • Consumer review networks (Trustpilot, Angi, Choice)
  • Blogging and publishing networks (Medium, Twitter)
  • Discussion forums (Reddit, Quora, HackerNews)
  • Relationship networks (Tinder, Bumble)

ประโยชน์ของการพัฒนาแอปโซเชียลมีเดีย

การสร้างแอปโซเชียลมีเดียมีประโยชน์หลายประการ ไม่ว่าจะเป็นการสร้างรายได้จากโฆษณา การทำเงินจากฟีเจอร์พิเศษ การเก็บข้อมูลผู้ใช้ที่มีค่าเพื่อวิเคราะห์เชิงลึก การได้สปอนเซอร์ร่วมกับบริษัทอื่น และอื่น ๆ อีกมากมาย

อีกหนึ่งข้อดีของแอปโซเชียลมีเดียคือมูลค่าในการขายต่อที่สูงมาก หากแอปของคุณประสบความสำเร็จในระดับหนึ่ง (มีฐานผู้ใช้พอสมควร) คุณก็สามารถขายต่อให้กับบริษัทอื่นได้อย่างง่ายดาย ยกตัวอย่างเช่น Twitter เคยถูกซื้อไปในราคา 44 พันล้านดอลลาร์ และ MySpace ถูกซื้อไปที่ 87 ล้านดอลลาร์

ในมุมมองของนักพัฒนา การสร้างโซเชียลเน็ตเวิร์กในรูปแบบง่าย ๆ ช่วยให้คุณได้ทำความเข้าใจกับเครื่องมือที่ใช้งาน และทำให้รู้ว่าการพัฒนาอะไรแบบนี้จริง ๆ แล้วมีความยากเพียงใด

ฟีเจอร์สำคัญที่แอปโซเชียลมีเดียควรมี

แอปโซเชียลมีเดียอาจมีฟีเจอร์ที่หลากหลาย แต่ก็ยังมีฟีเจอร์พื้นฐานบางอย่างที่ทุกแอปควรมีเพื่อความสำเร็จ

บัญชีผู้ใช้

แอปโซเชียลมีเดียทุกแอปล้วนเปิดโอกาสให้ผู้ใช้สร้างบัญชีของตนเอง เมื่อผู้ใช้สร้างบัญชีเรียบร้อยแล้ว พวกเขาจะเพิ่มข้อมูลส่วนตัวและตั้งค่าต่าง ๆ ตามความต้องการของตนได้ เช่น เลือกใช้ฟีเจอร์ที่สนใจ เพิ่มความสนใจของตัวเอง หรือซ่อนคอนเทนต์บางส่วนเป็นต้น

ข้อดีจากมุมมองธุรกิจคือ เมื่อมีบัญชีผู้ใช้ คุณสามารถสร้าง “โปรไฟล์ผู้ใช้” ที่สามารถระบุได้ว่าผู้ใช้นั้นชอบอะไร มีปฏิสัมพันธ์กับใคร และใช้ข้อมูลเหล่านี้ในการปรับโฆษณาให้เหมาะกับผู้ใช้แต่ละคนได้

การเชื่อมต่อระหว่างผู้ใช้

แอปโซเชียลมีเดียจะเปิดให้ผู้ใช้เชื่อมต่อกัน เช่น เพิ่มเป็นเพื่อน ติดตาม หรือกด subscribe เมื่อผู้ใช้สองคนเชื่อมต่อกันแล้ว ระบบฟีดของแต่ละคนจะเปลี่ยนไปตามการเชื่อมต่อนั้น

การแชร์คอนเทนต์

จุดประสงค์ของแอปโซเชียลมีเดียทุกแอปคือการแชร์คอนเทนต์ ถ้าแอปของคุณไม่อำนวยความสะดวกในการแชร์คอนเทนต์ ผู้ใช้ก็ยากที่จะสนใจ

ในการพัฒนาแอปโซเชียลมีเดีย ควรให้ความสำคัญกับแนวปฏิบัติที่ดีที่สุดด้าน UI/UX การโพสต์อะไรบางอย่างควรทำได้ง่ายและใช้เวลาเพียงไม่กี่คลิก

การค้นหาและการค้นพบคอนเทนต์

อัลกอริทึมการค้นหาและการค้นพบคอนเทนต์ที่ดีเป็นหัวใจสำคัญของการสร้างแอปโซเชียลมีเดียที่ประสบความสำเร็จ

แอปควรเอื้อให้ผู้ใช้ค้นหาคอนเทนต์ที่สนใจได้ง่าย และควรมีระบบฟีดที่ปรับให้เหมาะกับความสนใจของผู้ใช้ พร้อมฟังก์ชันการค้นหาที่หลากหลาย

การแจ้งเตือน

คุณควรพิจารณาใช้การแจ้งเตือนแบบพุช (Push Notifications) เพื่อสร้างการมีส่วนร่วมและกระตุ้นให้ผู้ใช้กลับมาใช้งาน

การแจ้งเตือนแบบพุชเป็นช่องทางสื่อสารที่มีประสิทธิภาพ ช่วยแจ้งผู้ใช้เมื่อมีเหตุการณ์ต่าง ๆ เช่น มีเพื่อนโพสต์ มีอีเวนต์ใหม่ หรือเมื่อผู้ใช้ขาดการใช้งานไปนาน เป็นต้น

หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับ Push Notifications และวิธีผสานเข้ากับโปรเจกต์ของคุณ สามารถดูได้ที่ What are Push Notifications?

วิธีสร้างแอปโซเชียลมีเดีย

ในส่วนของบทความนี้ เราจะมาเรียนรู้การสร้างแอปโซเชียลมีเดียทีละขั้นตอน โดยใช้ Back4app เป็นแบ็กเอนด์ และใช้ React ร่วมกับเฟรมเวิร์ก Next.js เป็นฟรอนต์เอนด์

สิ่งที่ต้องเตรียม

เทคสแตกที่เราจะใช้มีดังนี้:

  • ความคุ้นเคยกับ JavaScript ES6
  • ความคุ้นเคยกับ React/Next.js และ React hooks
  • ความเข้าใจพื้นฐานเกี่ยวกับ Backend as a Service (BaaS)
  • Node.js v16+ ติดตั้งบนเครื่อง และมีเครื่องมือแก้ไขโค้ดพร้อม

Back4app คืออะไร?

Back4app เป็น Low-code backend ที่ยอดเยี่ยมสำหรับการสร้างเว็บและโมบายล์แอปสมัยใหม่ได้อย่างรวดเร็ว มาพร้อมฟีเจอร์หลากหลาย ไม่ว่าจะเป็นฐานข้อมูลแบบเรียลไทม์ ระบบจัดการผู้ใช้ Cloud Code functions การแจ้งเตือนแบบพุช การผสานโซเชียล API SDKs และอื่น ๆ อีกมากมาย!

ด้วยการใช้ Back4app คุณสามารถส่งต่อภาระงานส่วนใหญ่ในฝั่งแบ็กเอนด์ได้ ทำให้คุณโฟกัสเฉพาะตรรกะหลักของธุรกิจและการพัฒนาฟรอนต์เอนด์

นอกจากนี้ คุณยังไม่ต้องกังวลเรื่องโครงสร้างพื้นฐานเบื้องหลังหรือการขยายระบบ เพราะ Back4app จัดการทั้งหมดให้ เหมาะอย่างยิ่งสำหรับเร่งกระบวนการพัฒนาแอปโซเชียลมีเดีย

Back4app มีแพ็กเกจฟรีที่เหมาะสำหรับการทดสอบหรือสร้างต้นแบบ และเมื่อแอปเติบโตขึ้น คุณสามารถอัปเกรดเป็นแพ็กเกจระดับพรีเมียมที่มี ราคาที่คาดการณ์ได้

ทำไมถึงควรใช้ Back4app ในการสร้างโซเชียลเน็ตเวิร์ก?

แนะนำโปรเจกต์

ในบทความนี้ เราจะสร้างโซเชียลเน็ตเวิร์กอย่างง่ายที่ให้ผู้ใช้สร้างบัญชี ยืนยันตัวตน ตั้งค่าโปรไฟล์ และสร้างโพสต์ได้

ฝั่งแบ็กเอนด์เราจะใช้ Back4app และฝั่งฟรอนต์เอนด์เราจะใช้ React ร่วมกับ Next.js เพื่อสร้างแอปโซเชียลมีเดีย

ขั้นแรก เราจะสร้างแอปบน Back4app ตั้งค่าโมเดลฐานข้อมูล จากนั้นจึงไปต่อที่ฝั่งฟรอนต์เอนด์

ฝั่งฟรอนต์เอนด์เราจะติดตั้ง Parse SDK ตั้งค่าระบบยืนยันตัวตน และสร้างหน้าต่าง ๆ เช่น หน้าเข้าสู่ระบบ หน้า sign-up และหน้าโปรไฟล์

ผลลัพธ์สุดท้ายจะมีหน้าตาแบบนี้:

ภาพตัวอย่าง Back4app Social Network

สร้างแอป

ขั้นตอนต่อไปนี้จำเป็นต้องมีบัญชี Back4app หากยังไม่มี ให้ สมัครฟรี ก่อน

เมื่อคุณเข้าสู่บัญชี Back4app แล้ว จะเห็นรายการแอปของคุณ ให้คลิก “Build new app” เพื่อเริ่มสร้างแอป

Back4app Build New App

Back4app มีตัวเลือกในการสร้างแอปอยู่ 2 แบบ:

  1. Backend as a Service (BaaS)
  2. Containers as a Service (CaaS)

BaaS คือโซลูชันแบ็กเอนด์ที่ใช้ Parse เต็มรูปแบบ ส่วน CaaS ใช้สำหรับดีพลอยแอปพลิเคชันที่บรรจุเป็น Docker container

เนื่องจากเรากำลังสร้างโซเชียลเน็ตเวิร์ก เราจึงเลือกใช้ “Backend as a Service”

Back4app Build BaaS

จากนั้น ตั้งชื่อแอปของคุณ เลือกประเภทฐานข้อมูลเป็น “NoSQL” แล้วคลิก “Create”

Back4app จะใช้เวลาประมาณ 2 นาทีในการเตรียมสิ่งต่าง ๆ ให้พร้อมสำหรับแอปของคุณ เมื่อเสร็จแล้ว ระบบจะพาคุณไปยังหน้าฐานข้อมูลของแอป

Back4app Database View

Database Classes

ต่อไป เราจะเตรียมฐานข้อมูลเพื่อการพัฒนาแอปโซเชียลมีเดีย

คุณจะสังเกตเห็นว่ามีคลาสอยู่แล้ว 2 คลาส ได้แก่ User และ Role โดยค่าเริ่มต้น คลาสทุกคลาสใน Back4app จะมีฟิลด์ต่อไปนี้:

+-----------+-------------------------------------------------------------------------+
| Name      | Explanation                                                             |
+-----------+-------------------------------------------------------------------------+
| objectId  | Object's unique identifier                                              |
+-----------+-------------------------------------------------------------------------+
| updatedAt | Date time of the object's last update.                                  |
+-----------+-------------------------------------------------------------------------+
| createdAt | Date time of object's creation.                                         |
+-----------+-------------------------------------------------------------------------+
| ACLs      | Allow you to control the access to the object (eg. read, update).       |
+-----------+-------------------------------------------------------------------------+

เราจะปรับ User เล็กน้อย โดยเพิ่มฟิลด์ description และ avatarUrl ผู้ใช้จะสามารถแก้ไขข้อมูลทั้งสองนี้ได้ภายหลังจากหน้า settings

ให้คลิกปุ่ม “+ Column” ด้านขวาบนของหน้าจอ แล้วเพิ่มฟิลด์สองฟิลด์นี้:

+-----------+-------------+--------------------+----------+
| Data type | Name        | Default value      | Required |
+-----------+-------------+--------------------+----------+
| String    | description | Another cool user! | yes      |
+-----------+-------------+--------------------+----------+
| String    | avatarUrl   | <some_image_url>   | yes      |
+-----------+-------------+--------------------+----------+

อย่าลืมแทนที่ <your_image_url> ด้วยลิงก์รูปภาพจริง ๆ ที่ลงท้ายด้วย .png, .jpg หรือ .jpeg หากยังนึกไม่ออก สามารถใช้ ลิงก์นี้ ได้

Back4app Database Add Column

ต่อไป ให้เราสร้างคลาสใหม่ชื่อ Post แต่ละโพสต์จะมีผู้เขียน (author) และข้อความ (content)

ให้คลิก “Create a class” ที่มุมซ้ายบนของหน้าจอ ตั้งชื่อว่า Post เลือก “Protected” และคลิก “Create class & add columns”

จากนั้นเพิ่มสองคอลัมน์ตามด้านล่าง:

+-----------------+---------+---------------+----------+
| Data type       | Name    | Default value | Required |
+-----------------+---------+---------------+----------+
| Pointer -> User | author  | <leave blank> | yes      |
+-----------------+---------+---------------+----------+
| String          | content | <leave blank> | yes      |
+-----------------+---------+---------------+----------+
Back4app Database Create Class

Back4app Database Security

เกี่ยวกับการรักษาความปลอดภัยของฐานข้อมูลใน Back4app มีสองแนวทางที่สามารถใช้ได้:

  1. Class Level Permissions (CLPs)
  2. Access Control Levels (ACLs)

CLPs จะโฟกัสการกำหนดสิทธิ์การเข้าถึงในระดับคลาส ทำให้ควบคุมการเข้าถึงและการแก้ไขข้อมูลได้อย่างละเอียด ส่วน ACLs จะให้หรือจำกัดสิทธิ์การเข้าถึงแก่อ็อบเจ็กต์แต่ละรายการ และอ้างอิงตามบทบาทหรือสิทธิ์ที่กำหนดไว้

หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับ Parse Security สามารถดูได้ที่บทความ Parse Server Security

เราต้องการให้เฉพาะผู้ใช้ที่เข้าสู่ระบบ (authenticated) เท่านั้นที่สามารถสร้างโพสต์ได้ และให้เฉพาะเจ้าของโพสต์เป็นผู้แก้ไขหรือลบโพสต์ของตนเองได้ เราจึงจะตั้งค่า CLPs สำหรับคลาส Post

ให้เลือกคลาส Post จากแถบด้านข้าง จากนั้นคลิกที่สามจุดมุมขวาบนของหน้าจอ เลือก “Security > Class Level Permissions” แล้วตั้งค่า CLPs ตามภาพ:

Back4app Post CLPs

เยี่ยม แค่นี้ก็เรียบร้อย ฝั่งแบ็กเอนด์ของเราพร้อมใช้งานแล้ว เห็นไหมว่าไม่ยากเลย

โค้ดฝั่งฟรอนต์เอนด์

ในส่วนนี้ เราจะสร้างฟรอนต์เอนด์สำหรับโซเชียลเน็ตเวิร์กของเรา

เริ่มโปรเจกต์

เริ่มต้นด้วยการใช้ create-next-app เพื่อบูตสแตรปโปรเจกต์ Next.js ใหม่:

$ npx create-next-app@latest back4app-social-network

√ Would you like to use TypeScript? ... No
√ Would you like to use ESLint? ... Yes
√ Would you like to use Tailwind CSS? ... No
√ Would you like to use `src/` directory? ... No
√ Would you like to use App Router? (recommended) ... No
√ Would you like to customize the default import alias? ... No

Created a new Next.js app in ~\back4app-social-network.

ตัวสร้างโปรเจกต์จะถามคำถามคุณหลายข้อ แนะนำให้เลือกใช้งานแค่ ESLint ส่วนออปชันอื่น ๆ อาจไม่จำเป็นสำหรับตัวอย่างนี้และจะทำให้โปรเจกต์ซับซ้อนขึ้น

โปรเจกต์ Next.js ที่ได้มาพร้อมไฟล์และโฟลเดอร์บางส่วนที่เราไม่จำเป็นต้องใช้ ให้ลบออกเพื่อลดขนาดโปรเจกต์ ได้แก่:

  • โฟลเดอร์ pages/api
  • โฟลเดอร์ styles
  • ไฟล์ public/next.svg
  • ไฟล์ public/vercel.svg

อย่าลืมลบการ import globals.css ในไฟล์ pages/_app.js ด้วย:

// pages/_app.js

import "@/styles/globals.css";  // remove this line

จากนั้นแทนที่โค้ดใน pages/index.js ด้วยเนื้อหาต่อไปนี้:

// pages/index.js

export default function Home() {
  return (
    <>
      <p>Hello world!</p>
    </>
  );
}

สั่งรันเซิร์ฟเวอร์พัฒนา Next:

$ next dev

สุดท้าย เปิดเบราว์เซอร์ไปที่ http://localhost:3000 ถ้าทุกอย่างถูกต้อง คุณจะเห็นข้อความ “Hello world!” ปรากฏขึ้น

ตั้งค่า ChakraUI

เพื่อให้การสร้าง UI รวดเร็วขึ้น เราจะใช้ ChakraUI ซึ่งเป็นไลบรารีสำหรับ React ที่มาพร้อมคอมโพเนนต์สำเร็จรูป ระบบสไตล์ ฮุคเฉพาะ และอื่น ๆ

ติดตั้งผ่าน NPM ดังนี้:

$ npm i @chakra-ui/react @chakra-ui/next-js @emotion/react @emotion/styled framer-motion

จากนั้นหุ้ม Component ของเราด้วย ChakraProvider:

// pages/_app.js

import {ChakraProvider} from "@chakra-ui/react";

function MyApp({Component, pageProps}) {
  return (
    <ChakraProvider>
      <Component {...pageProps} />
    </ChakraProvider>
  );
}

export default MyApp;

อย่าลืม import ไว้ด้านบนไฟล์ด้วย:

import {ChakraProvider} from "@chakra-ui/react";

สำหรับ Chakra ที่จะทำงานได้อย่างถูกต้อง เราต้องใส่สคริปต์ Color mode เพื่อให้การซิงก์ local storage ทำงานตามปกติ และหลีกเลี่ยงปัญหาหน้าจอกะพริบสี

แก้ไขไฟล์ pages/_document.js ดังนี้:

// pages/_document.js

import { Html, Head, Main, NextScript } from "next/document";
import {ColorModeScript, extendTheme} from "@chakra-ui/react";

export const theme = extendTheme();

export default function Document() {
  return (
    <Html lang="en">
      <Head />
      <body>
        <ColorModeScript initialColorMode={theme}/>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

แค่นี้ก็เสร็จขั้นตอนเริ่มต้นของ Chakra

React Icons

เพื่อให้แอปดูดีขึ้น เราจะติดตั้ง react-icons ซึ่งเป็นไลบรารีที่ช่วยให้เราดึงไอคอนยอดนิยมต่าง ๆ เข้ามาใช้งานได้ง่าย ไม่ว่าจะเป็น Ant, Bootstrap, Heroicons, Font Awesome เป็นต้น

ติดตั้งด้วยคำสั่ง:

$ npm install react-icons --save

เมื่อเสร็จแล้ว คุณสามารถนำเข้าและใช้งานไอคอนได้ตามต้องการ เช่น:

import {FaMusic} from "react-icons/fa";

return (
    <FaMusic/>
);

ดูรายชื่อไอคอนได้ที่ React Icons Docs

Layout และ Components

โดยทั่วไปแล้ว แอปโซเชียลเน็ตเวิร์กจะมี Layout ที่ใกล้เคียงกัน คือมี Header ส่วนบนและ Footer ส่วนล่างทุกหน้า เราจะมาสร้าง Layout กัน

เริ่มด้วยการสร้างโฟลเดอร์ใหม่ชื่อ components ที่โฟลเดอร์หลักของโปรเจกต์ และสร้างไฟล์ต่อไปนี้:

components/
├── header.js
├── footer.js
└── layout.js

เติมเนื้อหาในไฟล์ header.js ดังนี้:

// components/header.js

import NextLink from "next/link";
import {Box, Container, Divider, Heading, HStack, Link} from "@chakra-ui/react";

export default function Header() {
  return (
    <Box py={4}>
      <Container 
        maxW="container.lg" 
        display="flex" 
        alignItems="center" 
        justifyContent="space-between"
      >
        <Heading as="h1" size="md">
          <Link as={NextLink} href="/">
            back4app-social-network
          </Link>
        </Heading>
        <HStack spacing="1em">
          <Heading size="sm">
            <Link as={NextLink} href="/login">
              Log in
            </Link>
          </Heading>
          <Heading size="sm">
            <Link as={NextLink} href="/signup">
              Sign up
            </Link>
          </Heading>
        </HStack>
      </Container>
      <Divider my={4}/>
    </Box>
  );
}

จากนั้นในไฟล์ footer.js:

// components/footer.js

import NextLink from "next/link";
import {Box, Container, Divider, Heading,
  HStack, Link, Tag, Text, VStack} from "@chakra-ui/react";
import {FaGithub} from "react-icons/fa";

export default function Footer() {
  return (
    <Box py={4}>
      <Divider my={4}/>
      <Container
        maxW="container.lg"
        display="flex"
        justifyContent="space-between"
        alignItems="center"
      >
        <VStack alignItems="left">
          <Heading size="sm">
            A simple social network powered by Back4app.
          </Heading>
          <Link
            as={NextLink}
            href="https://blog.back4app.com/how-to-develop-a-social-media-app/"
          >
            Click here to learn how to build it!
          </Link>
        </VStack>
        <Link href="https://github.com/duplxey/back4app-social-network">
          <Tag background="black" color="white" py={2}>
            <HStack>
              <FaGithub size="1.5em"/>
              <Text>View on GitHub</Text>
            </HStack>
          </Tag>
        </Link>
      </Container>
    </Box>
  );
}

ในโค้ดข้างต้น เราใช้คอมโพเนนต์ที่มีมาให้ของ Chakra เพื่อสร้าง Header และ Footer แบบเรียบง่าย และเนื่องจากเราใช้ Next.js จึงนำ Link ของ Chakra มาทำงานร่วมกับ Link ของ Next

สุดท้าย สร้าง layout.js เพื่อประกอบ Header และ Footer เข้าด้วยกัน:

// components/layout.js

import {Container} from "@chakra-ui/react";
import Header from "@/components/header";
import Footer from "@/components/footer";

export default function Layout({children}) {
  return (
    <>
      <Header/>
      <Container maxW="container.lg">
        {children}
      </Container>
      <Footer/>
    </>
  );
}

จากนั้นนำ Layout ไปใช้งานใน index.js:

// pages/index.js

import Layout from "@/components/layout";

export default function Home() {
  return (
    <Layout>
      <p>Hello world!</p>
    </Layout>
  );
}

รอให้ Next recompile เสร็จแล้วเข้าไปที่ http://localhost:3000 ถ้าถูกต้อง เราจะเห็น Layout ใหม่ที่เราได้เพิ่มไว้

ตัวอย่าง Back4app Social Network Hello World

ตั้งค่า Parse.js

เริ่มต้นด้วยการติดตั้ง Parse ผ่าน NPM:

$ npm install parse

จากนั้นเพิ่มการตั้งค่า Parse ต่อจากส่วน import ในไฟล์ _app.js:

// pages/_app.js

// ...

import Parse from "parse/dist/parse";

const PARSE_APPLICATION_ID = process.env.NEXT_PUBLIC_PARSE_APPLICATION_ID;
const PARSE_JAVASCRIPT_KEY = process.env.NEXT_PUBLIC_PARSE_JAVASCRIPT_KEY;
Parse.initialize(PARSE_APPLICATION_ID, PARSE_JAVASCRIPT_KEY);
Parse.serverURL = "https://parseapi.back4app.com/";

// ...

เราไม่ได้ฮาร์ดโค้ดตัวแปร แต่ใช้ตัวแปรสภาพแวดล้อม (environmental variables) ใน Next.js ซึ่งจะโหลดโดยอัตโนมัติจากไฟล์ .env.local

ให้สร้างไฟล์ชื่อ .env.local ที่โฟลเดอร์หลักของโปรเจกต์ แล้วใส่ข้อมูลดังนี้:

NEXT_PUBLIC_PARSE_APPLICATION_ID=<parse_app_id>
NEXT_PUBLIC_PARSE_JAVASCRIPT_KEY=<parse_javascript_key>

อย่าลืมแทนที่ <parse_app_id> และ <parse_javascript_key> ด้วยค่าจริงของคุณเอง โดยไปที่แอป Back4app และดูที่ “App Settings > Security & Keys”

Context

เพื่อหลีกเลี่ยงการต้องส่งต่อ Parse instance ผ่าน props หลายชั้น เราจะใช้ React context แทน ซึ่งช่วยให้ส่งข้อมูลจากคอมโพเนนต์หนึ่งไปยังอีกคอมโพเนนต์ได้โดยไม่ต้องผ่าน props

สร้างโฟลเดอร์ชื่อ context และไฟล์ parseContext.js ภายใน:

// context/parseContext.js

import {createContext} from "react";

const ParseContext = createContext();

export default ParseContext;

จากนั้นหุ้ม Component ของเราด้วย ParseContext.Provider และส่ง Parse instance ไปด้วย:

// pages/_app.js

// ...

function MyApp({Component, pageProps}) {
  return (
    <ChakraProvider>
      <ParseContext.Provider value={Parse}>
        <Component {...pageProps} />
      </ParseContext.Provider>
    </ChakraProvider>
  );
}

export default MyApp;

อีกครั้ง อย่าลืม import ParseContext ไว้ด้านบนไฟล์:

import ParseContext from "@/context/parseContext";

ขณะนี้ เราสามารถเรียกใช้ Parse instance ได้ผ่าน useContext() ในคอมโพเนนต์ต่าง ๆ ลองทดสอบการเชื่อมต่อใน index.js ดู

แทนที่โค้ดใน pages/index.js ด้วย:

// pages/index.js

import {useContext} from "react";
import ParseContext from "@/context/parseContext";
import {Button} from "@chakra-ui/react";
import Layout from "@/components/layout";

export default function Home() {

  const parse = useContext(ParseContext);

  async function testConnection() {
    try {
      await new parse.Query("TestClass").first();
      console.log("Connection successful");
    } catch (error) {
      console.error("Connection failed: " + error);
    }
  }

  return (
    <Layout>
      <p>Hello world!</p>
      <Button onClick={() => testConnection()}>Parse.js test</Button>
    </Layout>
  );
}

รอให้ Next recompile จากนั้นเข้าไปที่ http://localhost:3000 เปิดคอนโซล แล้วกดปุ่ม “Parse.js test” ถ้าทุกอย่างถูกต้อง จะมีข้อความ “Connection successful” แสดงขึ้นมา

การยืนยันตัวตน (Authentication)

อย่างที่กล่าวไว้ในส่วน “Back4app คืออะไร?” Back4app มีระบบยืนยันตัวตนในตัวอยู่แล้ว การตั้งค่าระบบล็อกอิน/ล็อกเอาต์ด้วย Parse SDK จึงทำได้ง่ายมาก และ Parse จะจัดการเซสชันให้โดยอัตโนมัติ

เรามาสร้างฟอร์มสำหรับการยืนยันตัวตนกัน

เริ่มด้วยการสร้างไฟล์ signup.js ในโฟลเดอร์ pages และใส่โค้ดดังนี้:

// pages/signup.js

import NextLink from "next/link";
import {useState} from "react";
import {Button, Card, CardBody, CardFooter, CardHeader, FormControl, 
    FormLabel, Heading, HStack, Input, Link, Text, VStack,
} from "@chakra-ui/react";
import {FaUserPlus} from "react-icons/fa";
import Layout from "@/components/layout";

export default function SignUp() {

  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");

  const onSubmit = async (event) => {
    // implement logic
  };

  return (
    <Layout>
      <Card>
        <CardHeader>
          <HStack>
            <FaUserPlus/>
            <Heading as="h2" size="md"> Sign up</Heading>
          </HStack>
        </CardHeader>
        <CardBody py={0}>
          <VStack spacing="1em" alignItems="left">
            <FormControl>
              <FormLabel>Username</FormLabel>
              <Input
                placeholder="Username"
                value={username}
                onChange={(e) => setUsername(e.target.value)}
              />
            </FormControl>
            <FormControl>
              <FormLabel>Password</FormLabel>
              <Input
                type="password"
                placeholder="Password"
                value={password}
                onChange={(e) => setPassword(e.target.value)}
              />
            </FormControl>
          </VStack>
        </CardBody>
        <CardFooter 
            w="full" 
            display="flex" 
            alignItems="center" 
            justifyContent="space-between"
        >
          <Text>
            Already have an account?{" "}
            <Link as={NextLink} href="/login">
              Log in
            </Link>
          </Text>
          <Button colorScheme="teal" onClick={onSubmit}>Sign up</Button>
        </CardFooter>
      </Card>
    </Layout>
  );
}

โค้ดข้างต้นจะสร้างหน้า /signup ที่มีฟอร์มสำหรับสมัครบัญชี โดยให้ผู้ใช้กรอก username และ password

ต่อมาให้แก้ pages/signup.js เพื่อเพิ่มลอจิกดังนี้:

// pages/signup.js

// ...

import {useRouter} from "next/router";
import {useContext, useEffect} from "react";
import ParseContext from "@/context/parseContext";

export default function SignUp() {

  const router = useRouter();
  const parse = useContext(ParseContext);

  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");

  // redirect the user if already logged in
  useEffect(() => {
    (async () => {
      if (parse.User.current() !== null) {
        await router.push("/");
      }
    })();
  }, [router, parse.User]);

  const onSubmit = async (event) => {
    event.preventDefault();

    if (!username || !password) {
      console.error("Please fill out all the fields.");
      return;
    }

    try {
      await parse.User.signUp(username, password).then(() => {
        router.push("/");
        console.log("Successfully signed up.");
      });
    } catch (error) {
      console.error(error.message);
    }
  };

  return (
    // ...
  );
}
  1. หากผู้ใช้ล็อกอินอยู่แล้ว จะ redirect ไปที่ / โดยใช้ฮุค useRouter() ของ Next
  2. เราแก้ฟังก์ชัน onSubmit() ให้เรียก User.signUp() ซึ่งจะสร้างเซสชันผู้ใช้และเก็บคุกกี้ในเบราว์เซอร์ให้โดยอัตโนมัติ

ให้ทำแบบเดียวกันกับไฟล์ pages/login.js โดยดูโค้ดตัวอย่างได้ใน GitHub repo

เยี่ยม ตอนนี้ระบบยืนยันตัวตนเกือบเสร็จแล้ว ขั้นต่อไปคือปรับ header.js นิดหน่อยเพื่อแสดงชื่อผู้ใช้เมื่อผู้ใช้ล็อกอิน หรือแสดงลิงก์ Log in/Sign up หากยังไม่ล็อกอิน

แก้ components/header.js ดังนี้:

// components/header.js

import NextLink from "next/link";
import {Avatar, Box, Container, Divider, Heading, HStack, Link} from "@chakra-ui/react";
import {useContext, useEffect, useState} from "react";
import ParseContext from "@/context/parseContext";

export default function Header() {

  const parse = useContext(ParseContext);
  const [user, setUser] = useState(null);

  useEffect(() => {
    setUser(parse.User.current());
  }, [parse.User]);

  return (
    <Box py={4}>
      <Container 
        maxW="container.lg" 
        display="flex" 
        alignItems="center" 
        justifyContent="space-between"
      >
        <Heading as="h1" size="md">
          <Link as={NextLink} href="/">
            back4app-social-network
          </Link>
        </Heading>
        {user != null ? (
          <HStack>
            <Avatar 
              size="sm" 
              name={user.attributes.username} 
              src={user.attributes.avatarUrl}
            />
            <Heading size="sm">
              <Link as={NextLink} href="/settings">
                {user.attributes.username}
              </Link>
            </Heading>
          </HStack>
        ) : (
          <HStack spacing="1em">
            <Heading size="sm">
              <Link as={NextLink} href="/login">
                Log in
              </Link>
            </Heading>
            <Heading size="sm">
              <Link as={NextLink} href="/signup">
                Sign up
              </Link>
            </Heading>
          </HStack>
        )}
      </Container>
      <Divider my={4}/>
    </Box>
  );
}

รอให้ Next recompile แล้วทดสอบสร้างบัญชีใหม่ จะเห็นว่าหัวข้อใน Header เปลี่ยนไปตามสถานะล็อกอิน

ตอนนี้เรายังไม่ได้ทำระบบล็อกเอาต์ ดังนั้นถ้าต้องการล็อกเอาต์ ต้องลบคุกกี้ด้วยตัวเองก่อน

Back4app Social Network Dynamic Header

เมื่อคุณสร้างบัญชีเสร็จแล้ว ลองกลับไปดูฐานข้อมูลใน Back4app ตรงคลาส User จะเห็นว่ามีผู้ใช้ใหม่ถูกสร้างขึ้น

Back4app Database New User

หน้าตั้งค่าผู้ใช้ (User Settings)

ต่อไป เราจะทำให้ฟิลด์ description และ avatarUrl (ที่เราเพิ่มในคลาส User) สามารถแก้ไขได้

สร้างไฟล์ใหม่ชื่อ settings.js ในโฟลเดอร์ pages และใส่โค้ดต่อไปนี้:

// pages/settings.js

import React, {useContext, useEffect, useState} from "react";
import {useRouter} from "next/router";
import {Button, Card, CardBody, CardFooter, CardHeader, FormControl, FormLabel,
  Heading, HStack, Input, VStack} from "@chakra-ui/react";
import {FaCog} from "react-icons/fa";
import ParseContext from "@/context/parseContext";
import Layout from "@/components/layout";

export default function Settings() {

  const router = useRouter();
  const parse = useContext(ParseContext);

  const [description, setDescription] = useState("");
  const [avatarUrl, setAvatarUrl] = useState("");

  useEffect(() => {
    (async () => {
      const user = parse.User.current();

      // redirect the user if not logged in
      if (user === null) {
        await router.push("/");
        return;
      }

      // load data from the database
      setDescription(await user.get("description"));
      setAvatarUrl(await user.get("avatarUrl"));
    })();
  }, [router, parse.User]);

  const onSave = async () => {
    const user = parse.User.current();
    user.set("description", description);
    user.set("avatarUrl", avatarUrl);
    await user.save();

    console.log("Successfully saved settings.");
  };

  const onLogout = async () => {
    await parse.User.logOut();
    await router.push("/");

    console.log("Successfully logged out.");
  };

  return (
    <Layout>
      <Card>
        <CardHeader>
          <HStack>
            <FaCog/>
            <Heading as="h2" size="md"> Settings</Heading>
          </HStack>
        </CardHeader>
        <CardBody py={0}>
          <VStack spacing="1em">
            <FormControl>
              <FormLabel>Description</FormLabel>
              <Input
                placeholder="Description"
                value={description}
                onChange={e => setDescription(e.target.value)}
              />
            </FormControl>
            <FormControl>
              <FormLabel>Avatar URL</FormLabel>
              <Input
                placeholder="Avatar URL"
                value={avatarUrl}
                onChange={e => setAvatarUrl(e.target.value)}
              />
            </FormControl>
          </VStack>
        </CardBody>
        <CardFooter display="flex" justifyContent="right">
          <HStack>
            <Button colorScheme="red" onClick={onLogout}>Log out</Button>
            <Button colorScheme="teal" onClick={onSave}>Save</Button>
          </HStack>
        </CardFooter>
      </Card>
    </Layout>
  );
}
  1. เราใช้ React useEffect() เพื่อเช็กว่าผู้ใช้ล็อกอินหรือยัง ถ้ายัง ก็ redirect ไปหน้าแรก นอกจากนี้ยังโหลดข้อมูล description และ avatarUrl ของผู้ใช้มาใส่ใน state
  2. เราสร้างฟังก์ชัน onSave() เพื่ออัปเดตข้อมูลผู้ใช้ในฐานข้อมูลให้ตรงกับ state
  3. เราสร้างฟังก์ชัน onLogout() เพื่อล็อกเอาต์โดยเรียก parse.User.logOut() ซึ่งจะลบเซสชันออกจากฐานข้อมูลและลบคุกกี้จากเบราว์เซอร์

โพสต์

ส่วนสุดท้ายที่ต้องทำเพื่อให้แอปโซเชียลเน็ตเวิร์กง่าย ๆ ของเราสมบูรณ์ก็คือการเพิ่มฟีเจอร์โพสต์ เราได้สร้างคลาสในฐานข้อมูลเรียบร้อยแล้ว ตอนนี้แค่ทำหน้าฟอร์มสร้างโพสต์และดึงข้อมูลโพสต์มาแสดง

ก่อนอื่น สร้างคอมโพเนนต์ใหม่ชื่อ post.js:

// components/post.js

import {Avatar, Box, Card, CardBody, CardHeader, Heading, HStack, Text} from "@chakra-ui/react";

export default function Post(props) {
  return (
    <Card mt={2}>
      <CardHeader pb={0}>
        <HStack spacing="1em">
          <Avatar name={props.author.username} src={props.author.avatarUrl}/>
          <Box>
            <Heading size="sm">{props.author.username}</Heading>
            <Text>{props.author.description}</Text>
          </Box>
        </HStack>
      </CardHeader>
      <CardBody>
        <Text>{props.content}</Text>
      </CardBody>
    </Card>
  );
}

จากนั้นแก้ไข index.js เพื่อแสดงฟอร์มสร้างโพสต์และรายชื่อโพสต์:

// pages/index.js

import {useContext, useEffect, useState} from "react";
import {Alert, AlertIcon, Button, Card, CardBody, CardFooter,
  CardHeader, Heading, HStack, Textarea} from "@chakra-ui/react";
import {FaPlus} from "react-icons/fa";
import ParseContext from "@/context/parseContext";
import Layout from "@/components/layout";
import Post from "@/components/post";

export default function Home() {

  const parse = useContext(ParseContext);

  const [user, setUser] = useState(null);
  const [postContent, setPostContent] = useState("");
  const [posts, setPosts] = useState([]);

  const onCreatePost = async () => {
      // implement logic
  };

  return (
    <Layout>
      {user ? (
        <Card mb={2}>
          <CardHeader>
            <HStack>
              <FaPlus/>
              <Heading as="h2" size="md"> Create post</Heading>
            </HStack>
          </CardHeader>
          <CardBody py={0}>
            <Textarea
              placeholder="What's on your mind?"
              value={postContent}
              onChange={(event) => setPostContent(event.target.value)}
            />
          </CardBody>
          <CardFooter display="flex" justifyContent="right">
            <Button colorScheme="teal" onClick={onCreatePost}>Post</Button>
          </CardFooter>
        </Card>
      ) : (
        <Alert status="warning" mb={2}>
          <AlertIcon/>
          You need to log in to create posts.
        </Alert>
      )}
      {posts.map(post => (
        <Post
          key={post.id}
          content={post.attributes.content}
          author={{...post.attributes.author.attributes}}
        />
      ))}
    </Layout>
  );
}

เพิ่มลอจิกในฟังก์ชัน onCreatePost() ดังนี้:

const onCreatePost = async () => {
  if (!user == null) return;

  const post = new parse.Object("Post");
  post.set("content", postContent);
  post.set("author", user);
  await post.save();

  setPostContent("");
  setPosts([post, ...posts]);
};

สุดท้าย เพิ่ม useEffect() เพื่อดึงข้อมูลโพสต์:

useEffect(() => {
  setUser(parse.User.current());

  (async () => {
    const posts = await new parse.Query("Post")
      .include("author").descending("createdAt").find();
    setPosts(posts);
  })();
}, []);

หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับ Parse Objects และ Queries สามารถดูได้ที่ Parse Documentation

จากนั้นสั่งรันเซิร์ฟเวอร์ Next อีกครั้ง:

$ next start

ลองสร้างโพสต์ตัวอย่างสักสองสามโพสต์ จากนั้นรีเฟรชหน้า หากทุกอย่างทำงานถูกต้อง โพสต์จะถูกเก็บไว้ในฐานข้อมูล และดึงกลับมาแสดงเมื่อคุณเปิดหน้าอีกครั้ง

Back4app Database Posts

สรุป

ในบทความนี้ เราได้สร้างแอปโซเชียลมีเดียง่าย ๆ ซึ่งให้ผู้ใช้สมัครบัญชี ล็อกอิน ตั้งค่าโปรไฟล์ และโพสต์เนื้อหาได้

เมื่อถึงตอนนี้ คุณควรเข้าใจวิธีการทำงานของ Back4app และรู้วิธีเริ่มต้นสร้างโซเชียลเน็ตเวิร์กแล้ว

โปรเจกต์ตัวอย่างในบทความนี้สามารถใช้เป็นโครงสร้างพื้นฐานสำหรับการต่อยอด เพิ่มฟีเจอร์ใหม่ เช่น ระบบไลก์/ดิสไลก์ ระบบแชร์ หรือระบบคอมเมนต์ เพื่อฝึกฝนเพิ่มเติม

โค้ดตัวอย่างทั้งหมดสามารถดูได้ที่ back4app-social-network ใน GitHub

ก้าวต่อไป

  1. ทำตาม บทความนี้ เพื่อดีพลอย Next.js ฟรอนต์เอนด์ของคุณไปยัง Back4app Containers
  2. ศึกษาการใช้ Cloud Code Functions เพื่อเพิ่มความสามารถขั้นสูงด้านแบ็กเอนด์
  3. เปิดใช้งานการ ยืนยันอีเมลผู้ใช้ เพื่อป้องกันบอต

Leave a reply

Your email address will not be published.