পাঠ ৪২ · ৫৮-এর মধ্যে · মডিউল ৯
Home / Courses / Full-Stack Web Frameworks / হাইড্রেশন -- সার্ভার ও ক্লায়েন্টের সংযোগ

হাইড্রেশন -- সার্ভার ও ক্লায়েন্টের সংযোগ

Hydration -- attaching real interactivity to server-rendered HTML on the client
৯ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • হাইড্রেশনের দুটি ফেজ -- সার্ভার ফেজ (static HTML + state serialize) ও ক্লায়েন্ট ফেজ (state parse + handler attach)
  • Python দিয়ে সিমুলেট করে দেখানো HTML মার্কআপ হাইড্রেশনের আগে-পরে অভিন্ন থাকে (স্ট্রিং ইকুয়ালিটি দিয়ে প্রমাণ)
  • ইভেন্ট-হ্যান্ডলার হাইড্রেশনের আগে None (কল করলে TypeError), পরে callable -- দুটো অবস্থাই সরাসরি প্রমাণ করা
  • M9-এর CSR/SSR/SSG/ISR-এর সাথে হাইড্রেশনের সম্পর্ক -- কেন SSR/SSG-এর পরেও JS দরকার হয়

১ · হাইড্রেশন কী ও কেন দরকার

হাইড্রেশনHydrationসার্ভার-রেন্ডার করা static HTML-কে ক্লায়েন্টে ইন্টারেক্টিভ করে তোলার প্রক্রিয়া -- একই মার্কআপে real ইভেন্ট-হ্যান্ডলার সংযুক্ত করে, নতুন করে রেন্ডার না করেই। L39 (SSR) ও L40 (SSG)-এ দেখা গিয়েছিল সার্ভার সম্পূর্ণ, প্রস্তুত HTML পাঠায় -- ব্যবহারকারী তৎক্ষণাৎ কন্টেন্ট দেখতে পায়। কিন্তু সেই HTML নিজে থেকে ইন্টারেক্টিভ নয় -- একটি বাটনে ক্লিক করলে কিছু ঘটবে না, যতক্ষণ না ব্রাউজারে JavaScript চালু হয়ে সেই বাটনের সাথে একটি real ইভেন্ট-হ্যান্ডলার সংযুক্ত করে। এই "সংযুক্ত করার" ধাপটিই হাইড্রেশন -- এটি মার্কআপ বদলায় না, শুধু তার সাথে আচরণ জুড়ে দেয়।

সার্ভার ফেজ
static HTML স্ট্রিং তৈরি হয় (render() দিয়ে) + initial state json.dumps দিয়ে সিরিয়ালাইজ হয়ে পাঠানো হয়।
ক্লায়েন্ট ফেজ
state ব্লব json.loads দিয়ে পার্স হয়, real ফাংশন DOM এলিমেন্টের সাথে সংযুক্ত হয় -- হাইড্রেশন সম্পন্ন।

২ · হাইড্রেশন সিমুলেশন -- HTML অভিন্ন, হ্যান্ডলার আগে-পরে ভিন্ন

নিচের কোড সেলে একটি "কাউন্টার" উইজেট সিমুলেট করা হয়েছে। সার্ভার ফেজে render() কল হয়ে static HTML তৈরি হয় এবং json.dumps দিয়ে state সিরিয়ালাইজ হয়। একটি button_element ডিকশনারি ব্রাউজারের একটি DOM এলিমেন্টের স্ট্যান্ড-ইন -- হাইড্রেশনের আগে এর onclick মান None। তারপর ক্লায়েন্ট ফেজে state পার্স হয় ও একটি real handle_click ফাংশন সংযুক্ত হয় -- এবং হাইড্রেশনের আগে-পরে বাটনে ক্লিক করার চেষ্টা করে ফলাফল তুলনা করা হয়।

Python
import json

def render(template, data):
    """L38-L41-এর একই ধারণা -- str.format() ভিত্তিক টেমপ্লেট-রেন্ডার।"""
    return template.format(**data)


template = "<div id='counter-widget'><p>আপনি ক্লিক করেছেন: {count} বার</p><button id='inc-btn'>+1</button></div>"


# ---------- ধাপ ১: SERVER ফেজ -- static HTML + সিরিয়ালাইজড initial state ----------
print("=== SERVER ফেজ ===")
server_state = {"count": 0}
html_from_server = render(template, server_state)
initial_state_blob = json.dumps(server_state)
print(f"[server]  render() কল হলো -- static HTML: {html_from_server}")
print(f"[server]  initial state সিরিয়ালাইজ হলো (JSON): {initial_state_blob}")

# সিমুলেটেড DOM বাটন -- এখনো কোনো ইভেন্ট-হ্যান্ডলার সংযুক্ত হয়নি
button_element = {"id": "inc-btn", "onclick": None}
print(f"\n[client, হাইড্রেশনের আগে]  button_element['onclick'] = {button_element['onclick']}")
print("[client, হাইড্রেশনের আগে]  বাটনে ক্লিক করার চেষ্টা করা হচ্ছে...")
try:
    button_element["onclick"]()
    click_before_hydration_worked = True
except TypeError as err:
    click_before_hydration_worked = False
    print(f"[client, হাইড্রেশনের আগে]  ব্যর্থ -- TypeError: {err}")


# ---------- ধাপ ২: CLIENT ফেজ -- হাইড্রেশন ----------
print("\n=== CLIENT ফেজ -- হাইড্রেশন শুরু ===")
parsed_state = json.loads(initial_state_blob)
print(f"[client]  state blob পার্স হলো: {parsed_state}")

# hydration-mismatch যাচাই: ক্লায়েন্ট একই ডেটা দিয়ে আবার render() কল করে
# দেখে সার্ভারের HTML-এর সাথে হুবহু মেলে কি না -- বাস্তব ফ্রেমওয়ার্ক (যেমন React)
# dev মোডে ঠিক এই যাচাইটিই করে, মার্কআপ পুনরায় তৈরি করে না
html_from_client_check = render(template, parsed_state)
markup_identical = html_from_server == html_from_client_check
print(f"[client]  যাচাই-render() কল হলো: {html_from_client_check}")
print(f"[client]  সার্ভার ও ক্লায়েন্ট HTML হুবহু অভিন্ন? {markup_identical}")

def handle_click():
    parsed_state["count"] += 1
    print(f"    [client]  ক্লিক হ্যান্ডলার চললো -- নতুন count = {parsed_state['count']}")

button_element["onclick"] = handle_click
print(f"[client]  ইভেন্ট-হ্যান্ডলার সংযুক্ত হলো -- হাইড্রেশন সম্পন্ন")
print(f"[client]  button_element['onclick'] এখন callable? {callable(button_element['onclick'])}")


# ---------- হাইড্রেশনের পরে যাচাই ----------
print("\n=== হাইড্রেশনের পরে ===")
print("[client, হাইড্রেশনের পরে]  বাটনে দুইবার ক্লিক করা হচ্ছে...")
button_element["onclick"]()
button_element["onclick"]()
click_after_hydration_worked = button_element["onclick"] is not None

print(f"\nহাইড্রেশনের আগে ক্লিক কাজ করেছিল? {click_before_hydration_worked}")
print(f"হাইড্রেশনের পরে হ্যান্ডলার callable আছে? {click_after_hydration_worked}")
print(f"HTML মার্কআপ অপরিবর্তিত ছিল (visual পরিবর্তন নেই)? {markup_identical}")
print(f"চূড়ান্ত count মান: {parsed_state['count']}")

    
হাতে-কলমে ট্রেস: হাইড্রেশনের আগে button_element["onclick"] হলো None, তাই button_element["onclick"]() আসলে None() কল করার চেষ্টা করে, যা TypeError: 'NoneType' object is not callable রেইজ করে -- click_before_hydration_worked কখনো True হওয়ার সুযোগই পায় না, except-এ পড়ে False হয়। হাইড্রেশনের পরে button_element["onclick"] এখন সত্যিকারের handle_click ফাংশন -- দুইবার কল করায় parsed_state["count"] ০ → ১ → ২ হয়। আর html_from_server ও html_from_client_check দুটোই একই template ও একই মান (count=0) দিয়ে তৈরি, তাই markup_identical সবসময় True -- হাইড্রেশন HTML পাল্টায়নি, শুধু আচরণ যোগ করেছে।

৩ · কেন SSR/SSG-এর পরেও JavaScript দরকার হয়

এই সিমুলেশনটি দেখায় কেন "সার্ভার-রেন্ডার করা মানেই JS দরকার নেই" ধারণাটি ভুল। SSR বা SSG সার্ভারে HTML তৈরি করে প্রথম-লোডের গতি বাড়ায়, কিন্তু বাটন-ক্লিক, ফর্ম-সাবমিট, বা যেকোনো ইন্টারঅ্যাকশনের জন্য এখনো ক্লায়েন্ট-সাইড JavaScript চালু হয়ে হ্যান্ডলার সংযুক্ত করতে হয় -- এটাই হাইড্রেশন। এর ভাষাগত ভিত্তি (ফাংশন, ইভেন্ট, ক্লোজার) JavaScript Programming কোর্সে তৈরি হয়েছে। হাইড্রেশনের সময় যদি সার্ভার ও ক্লায়েন্টের রেন্ডার ফলাফল না মেলে (যাকে "hydration mismatch" বলা হয়), সেটি বাস্তব ফ্রেমওয়ার্কে বাগের লক্ষণ -- উপরের কোডে markup_identical চেকটি ঠিক সেই ধারণার একটি সরল সংস্করণ।

মূল কথা · Key takeaway

হাইড্রেশন দুই ফেজে ঘটে -- সার্ভার static HTML ও সিরিয়ালাইজড state পাঠায়, ক্লায়েন্ট সেই state পার্স করে real ইভেন্ট-হ্যান্ডলার সংযুক্ত করে। HTML মার্কআপ প্রক্রিয়া জুড়ে অপরিবর্তিত থাকে (স্ট্রিং ইকুয়ালিটি দিয়ে প্রমাণিত), কিন্তু ইন্টারঅ্যাকশন হ্যান্ডলার হাইড্রেশনের আগে অনুপস্থিত (None) থেকে পরে callable হয়ে ওঠে। M9-এর পুরো আর্ক (CSR → SSR → SSG → ISR → হাইড্রেশন) একসাথে দেখায়: একই render() ধারণা যেকোনো রেন্ডারিং স্ট্র্যাটেজিতে কাজ করে -- পার্থক্য শুধু কোথায়, কখন, ও কতবার সেটি কল হয়।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন -- তারপর "→ উত্তর" চাপুন।

প্র ০১ উপরের কোডে markup_identical কেন সবসময় True হবে, এমনকি কোড একাধিকবার চালালেও?

কারণ html_from_server ও html_from_client_check দুটোই ঠিক একই template ব্যবহার করে, এবং parsed_state আসলে server_state-এর JSON-এ সিরিয়ালাইজ-তারপর-ডিসিরিয়ালাইজ করা হুবহু কপি (count: 0) -- হাইড্রেশন-চেকের সময় এখনো কোনো ক্লিক হয়নি, তাই মান বদলায়নি। একই টেমপ্লেট + একই ডেটা মানেই render()-এর ফলাফল একই।

প্র ০২ হাইড্রেশনের আগে button_element["onclick"]() কল করলে ঠিক কোন এররটি ওঠে, এবং কেন?

TypeError: 'NoneType' object is not callable ওঠে -- কারণ button_element["onclick"]-এর মান তখনো None, এবং Python-এ None-কে ফাংশনের মতো কল করার চেষ্টা (None()) বৈধ নয়। এটাই বাস্তব ব্রাউজারে হাইড্রেশনের আগে বাটনে ক্লিক করার সমতুল্য -- কিছুই ঘটে না, কারণ কোনো হ্যান্ডলার সংযুক্তই হয়নি।

প্র ০৩ যদি সার্ভার ও ক্লায়েন্ট ভিন্ন ডেটা দিয়ে render() কল করত (যেমন সার্ভারে count=0, কিন্তু ক্লায়েন্ট ভুলবশত count=5 দিয়ে যাচাই করত), markup_identical-এ কী ঘটত?

তখন markup_identical False হতো, কারণ দুটি HTML স্ট্রিং-এ ভিন্ন সংখ্যা থাকত ("০ বার" বনাম "৫ বার")। বাস্তব ফ্রেমওয়ার্কে এটিকে "hydration mismatch" বলা হয় -- সার্ভার ও ক্লায়েন্টের রেন্ডার ফলাফল না মেলা একটি বাগের লক্ষণ, প্রায়ই ভুল বা সিঙ্ক-না-হওয়া initial state-এর কারণে ঘটে।

অনুশীলন

  1. চিন্তা করুন: হাইড্রেশন কেন "নতুন করে রেন্ডার করা" নয়, বরং "বিদ্যমান HTML-এর সাথে আচরণ সংযুক্ত করা" -- এই পার্থক্যটি ব্যবহারকারীর অভিজ্ঞতায় কেন গুরুত্বপূর্ণ?

    যদি হাইড্রেশন প্রতিবার নতুন করে সম্পূর্ণ HTML পুনরায় তৈরি করত, তাহলে ব্যবহারকারী একটি সংক্ষিপ্ত "ফ্ল্যাশ" বা পুনরায়-আঁকা দেখতে পেত -- যা দৃষ্টিকটু ও ধীর। হাইড্রেশন বরং বিদ্যমান, ইতিমধ্যে-দৃশ্যমান মার্কআপ পুনর্ব্যবহার করে, শুধু তার উপর ইভেন্ট-হ্যান্ডলার "বসিয়ে দেয়" -- ফলে ব্যবহারকারী সার্ভার-রেন্ডার করা কন্টেন্ট থেকে সরাসরি ইন্টারেক্টিভ কন্টেন্টে নিরবচ্ছিন্নভাবে যায়, কোনো ভিজ্যুয়াল ঝাঁকুনি ছাড়াই।

  2. পরীক্ষা করুন: উপরের কোড সেলে হাইড্রেশনের পরের অংশে আরও একটি (তৃতীয়) ক্লিক যোগ করুন (button_element["onclick"]() আরেকবার লিখে), Run চাপুন, এবং যাচাই করুন parsed_state['count'] এখন ৩ দেখাচ্ছে কি না।

    হ্যাঁ, তৃতীয়বার button_element["onclick"]() কল করলে handle_click আবার চলবে, parsed_state["count"]-কে ২ থেকে ৩-এ বাড়িয়ে দেবে -- কারণ হাইড্রেশনের পর onclick এখন একটি স্থায়ীভাবে সংযুক্ত, বারবার-কলযোগ্য ফাংশন, যা প্রতিবার কল হলে একই parsed_state ডিকশনারি আপডেট করে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

  • পরবর্তী পাঠ L43 GraphQL ফান্ডামেন্টাল -- M10 শুরু, REST-এর বাইরে API ডিজাইনের একটি ভিন্ন পথ দেখুন।
  • আগের পাঠ আবার দেখুন L41 ISR-এর রিভ্যালিডেশন-চেক আবার দেখে এই পাঠের হাইড্রেশন প্রক্রিয়ার সাথে সম্পূর্ণ M9 আর্ক মেলান।
  • কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ আর্কিটেকচার প্যাটার্ন, ফ্রন্ট-এন্ড/ব্যাক-এন্ড ফ্রেমওয়ার্ক ফান্ডামেন্টাল, স্টেট ম্যানেজমেন্ট, REST API, ORM, অথেন্টিকেশন, রেন্ডারিং স্ট্র্যাটেজি ও ডিপ্লয়মেন্ট।
  • সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git, Theory of Computation, Engineering Economics ও Full-Stack Web Frameworks -- সব এক জায়গায়।
আগের পাঠ
ইনক্রিমেন্টাল স্ট্যাটিক রিজেনারেশন ও হাইব্রিড রেন্ডারিং