হাইড্রেশন -- সার্ভার ও ক্লায়েন্টের সংযোগ
এই পাঠে যা শিখবেন
- হাইড্রেশনের দুটি ফেজ -- সার্ভার ফেজ (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 ফাংশন সংযুক্ত হয় -- এবং হাইড্রেশনের
আগে-পরে বাটনে ক্লিক করার চেষ্টা করে ফলাফল তুলনা করা হয়।
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 চেকটি ঠিক সেই ধারণার একটি সরল সংস্করণ।
হাইড্রেশন দুই ফেজে ঘটে -- সার্ভার 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-এর কারণে ঘটে।
অনুশীলন
-
চিন্তা করুন: হাইড্রেশন কেন "নতুন করে রেন্ডার করা" নয়, বরং "বিদ্যমান HTML-এর সাথে আচরণ
সংযুক্ত করা" -- এই পার্থক্যটি ব্যবহারকারীর অভিজ্ঞতায় কেন গুরুত্বপূর্ণ?
যদি হাইড্রেশন প্রতিবার নতুন করে সম্পূর্ণ HTML পুনরায় তৈরি করত, তাহলে ব্যবহারকারী একটি সংক্ষিপ্ত "ফ্ল্যাশ" বা পুনরায়-আঁকা দেখতে পেত -- যা দৃষ্টিকটু ও ধীর। হাইড্রেশন বরং বিদ্যমান, ইতিমধ্যে-দৃশ্যমান মার্কআপ পুনর্ব্যবহার করে, শুধু তার উপর ইভেন্ট-হ্যান্ডলার "বসিয়ে দেয়" -- ফলে ব্যবহারকারী সার্ভার-রেন্ডার করা কন্টেন্ট থেকে সরাসরি ইন্টারেক্টিভ কন্টেন্টে নিরবচ্ছিন্নভাবে যায়, কোনো ভিজ্যুয়াল ঝাঁকুনি ছাড়াই।
-
পরীক্ষা করুন: উপরের কোড সেলে হাইড্রেশনের পরের অংশে আরও একটি (তৃতীয়) ক্লিক যোগ করুন
(
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 -- সব এক জায়গায়।